한국GPT협회

AI, VBA, Python으로 끝내는 MS 오피스 자동화

프롬프트 엔지니어링부터 파이썬 자동화까지, AI 기반 업무 자동화의 전체 흐름

Part 1 프롬프트 → Part 2 엑셀 함수 → Part 3 파일 자동화 → Part 4 PPT → Part 5 워드 → Part 6 이메일 → Part 7 파이썬

1차시. 프롬프트 엔지니어링

학습 목표

  • 생성형 AI와 업무 자동화의 관계를 이해한다.
  • 프롬프트의 기본 구조(지시문과 맥락)를 구분할 수 있다.
  • 구조화된 프롬프트를 작성하여 원하는 결과를 얻을 수 있다.
  • Few-shot 프롬프팅 기법을 활용할 수 있다.
  • AI와 반복 수정(iteration) 방식으로 협업할 수 있다.

1. 생성형 AI와 업무 자동화

1.1 생성형 AI란?

생성형 AI(Generative AI)는 텍스트, 이미지, 코드 등 새로운 콘텐츠를 생성할 수 있는 인공지능입니다. 대표적으로 ChatGPT, Claude, Gemini 등이 있습니다.

1.2 업무 자동화에서의 역할

생성형 AI는 다음과 같은 업무를 자동화하는 데 활용됩니다.

  • 문서 작성: 보고서, 안내문, 이메일 초안
  • 데이터 처리: 엑셀 함수·수식 생성, VBA 코드 작성
  • 분석 보조: 데이터 해석, 요약, 시각화 코드 생성
  • 반복 업무 제거: 양식 변환, 파일 정리, 일괄 처리
핵심: AI가 좋은 결과를 내려면 사람이 좋은 질문(프롬프트)을 해야 합니다. 이것이 프롬프트 엔지니어링입니다.

2. 프롬프트의 기본 구조

프롬프트는 크게 지시문(Instruction)맥락(Context) 두 부분으로 구성됩니다.

2.1 지시문 (Instruction)

AI에게 무엇을 해달라는 명령입니다.

요소설명예시
행동무엇을 할 것인가"작성해줘", "요약해줘", "분류해줘"
형식어떤 형태로 줄 것인가"표로 정리해줘", "3줄로 요약해줘"
제약지켜야 할 조건"200자 이내로", "한국어로"

2.2 맥락 (Context)

AI가 정확한 결과를 내기 위해 필요한 배경 정보입니다.

요소설명예시
역할AI가 어떤 입장인가"너는 HR 담당자야"
대상누구를 위한 것인가"신입사원 대상"
상황어떤 상황인가"분기 실적 보고 시즌"
데이터참고할 자료구체적인 수치, 목록 등

2.3 지시문만 있는 프롬프트 vs 맥락이 포함된 프롬프트

나쁜 예시 (지시문만)

나쁜 프롬프트 예시
교육 안내문 써줘

좋은 예시 (지시문 + 맥락)

좋은 프롬프트 예시
너는 대기업 인사팀 사원이야.
다음 주 화요일에 진행하는 사내 엑셀 교육에 대한 안내문을 작성해줘.

- 교육명: 엑셀 데이터 분석 실무
- 일시: 2026년 3월 24일(화) 14:00~17:00
- 장소: 본사 8층 대회의실
- 대상: 전 직원 (선착순 30명)
- 신청 방법: 사내 인트라넷 > 교육 신청
- 마감: 3월 20일(금)

공식적이지만 딱딱하지 않은 톤으로, 300자 내외로 작성해줘.

3. 프롬프트 구조화 방법

3.1 하이픈(-)을 활용한 목록 구조

정보를 나열할 때 하이픈을 사용하면 AI가 각 항목을 명확히 구분합니다.

하이픈 목록 구조 예시
다음 조건에 맞는 체크리스트를 만들어줘.

- 대상: 신입사원 온보딩
- 기간: 입사 후 첫 1주일
- 항목 수: 10개
- 카테고리: 행정, IT, 팀 적응
- 형식: 체크박스가 있는 표

3.2 마크다운 제목(#)을 활용한 섹션 구조

긴 프롬프트에서 내용을 섹션별로 나눌 때 유용합니다.

마크다운 섹션 구조 예시
# 역할
너는 10년 경력의 HR 컨설턴트야.

# 작업
아래 직원 데이터를 분석하여 이직 위험군을 식별해줘.

# 데이터
- 직원 수: 1,470명
- 컬럼: 나이, 퇴사여부, 부서, 직무만족도, 월급, 초과근무, 근속연수

# 출력 형식
- 이직 위험 요인 3가지
- 각 요인별 근거
- 대응 방안 1줄씩

3.3 구조화 효과 비교

방식장점적합한 상황
하이픈(-)조건·항목을 깔끔하게 나열짧은 목록, 제약 조건
마크다운 제목(#)긴 프롬프트를 섹션으로 분리복잡한 요청, 역할+작업+형식 분리
번호(1, 2, 3)순서가 중요한 단계절차, 프로세스 설명

4. Few-shot 프롬프팅

4.1 Zero-shot vs One-shot vs Few-shot

방식예시 개수설명
Zero-shot0개예시 없이 지시만 제공
One-shot1개예시 1개 제공
Few-shot2~5개예시 여러 개 제공

예시를 제공하면 AI가 패턴을 학습하여 일관된 형식으로 결과를 생성합니다.

4.2 Zero-shot 예시

Zero-shot 프롬프트
직원 피드백을 긍정/부정/중립으로 분류해줘.

"팀 분위기가 좋아서 출근이 즐겁습니다."

AI가 형식을 자유롭게 결정하므로 결과가 일관되지 않을 수 있습니다.

4.3 One-shot 예시

One-shot 프롬프트
직원 피드백을 분류해줘. 아래 예시와 같은 형식으로 출력해줘.

예시)
피드백: "급여가 시장 대비 낮은 것 같습니다."
분류: 부정
키워드: 급여, 불만족

---

피드백: "팀 분위기가 좋아서 출근이 즐겁습니다."

4.4 Few-shot 예시

Few-shot 프롬프트
직원 피드백을 분류해줘. 아래 예시들과 같은 형식으로 출력해줘.

예시 1)
피드백: "급여가 시장 대비 낮은 것 같습니다."
분류: 부정
키워드: 급여, 불만족

예시 2)
피드백: "재택근무 제도가 잘 되어 있어요."
분류: 긍정
키워드: 근무환경, 만족

예시 3)
피드백: "교육 프로그램이 다양했으면 합니다."
분류: 중립
키워드: 교육, 개선요청

---

아래 피드백들을 같은 형식으로 분류해줘.

1. "팀 분위기가 좋아서 출근이 즐겁습니다."
2. "야근이 너무 잦아 힘듭니다."
3. "사내 동호회 활동이 활발합니다."
: 예시가 많을수록 AI의 출력이 일관되지만, 3~5개면 충분합니다. 너무 많으면 오히려 혼란을 줄 수 있습니다.

5. AI와 협업하는 반복 수정 방식

5.1 한 번에 완벽한 결과를 기대하지 마세요

프롬프트 엔지니어링의 핵심은 반복(iteration)입니다. 첫 결과가 만족스럽지 않으면 수정 요청을 통해 개선합니다.

5.2 반복 수정 패턴

반복 수정 패턴 예시
[1단계: 초안 요청]
사내 교육 안내문을 작성해줘.

[2단계: 구체화]
좀 더 공식적인 톤으로 바꿔줘. 교육 일시와 장소를 강조해줘.

[3단계: 형식 조정]
핵심 정보를 표로 정리하고, 하단에 문의처를 추가해줘.

[4단계: 세부 수정]
"신청 마감"을 빨간색 강조 표시로 바꾸고, 전체 길이를 200자로 줄여줘.

5.3 효과적인 수정 요청 표현

상황수정 요청 예시
너무 길 때"절반 길이로 줄여줘"
톤이 안 맞을 때"좀 더 캐주얼하게 / 공식적으로 바꿔줘"
형식 변경"표 형식으로 바꿔줘", "번호 매겨줘"
내용 추가"문의처와 마감일을 추가해줘"
내용 삭제"인사말 부분은 빼줘"
예시 추가"각 항목에 구체적 예시를 하나씩 넣어줘"

5.4 대화 맥락 활용

ChatGPT는 같은 대화 내에서 이전 내용을 기억합니다. 이를 활용하면 점진적으로 결과를 개선할 수 있습니다.

대화 맥락 활용 예시
처음: "HR 데이터 분석 보고서 목차를 만들어줘"
이어서: "2번 항목을 더 세분화해줘"
이어서: "각 항목에 예상 분량(페이지 수)을 추가해줘"
이어서: "이 목차에 맞는 서론을 500자로 써줘"

6. 문서 자동화의 출발점

6.1 프롬프트 엔지니어링이 자동화의 첫 단계인 이유

단계내용도구
1단계프롬프트로 원하는 결과를 정의ChatGPT
2단계엑셀 함수·수식 자동 생성ChatGPT + 엑셀
3단계VBA 코드로 반복 작업 자동화ChatGPT + VBA
4단계파일 분리·취합 자동화ChatGPT + VBA
이 교육의 2차시, 3차시에서 엑셀과 VBA를 활용한 자동화를 실습합니다. 프롬프트 엔지니어링은 이 모든 과정의 출발점입니다.

6.2 문서 종류별 프롬프트 전략

문서 종류핵심 맥락프롬프트 팁
안내문대상, 일시, 장소, 방법핵심 정보를 목록으로 제공
보고서목적, 데이터, 결론 방향역할 설정 + 출력 형식 지정
체크리스트대상, 기간, 카테고리항목 수와 분류 기준 제시
이메일수신자, 목적, 톤관계와 상황 설명

실습

실습 1: 짧고 모호한 프롬프트 vs 구조화된 프롬프트 비교

Step 1. ChatGPT에 아래 프롬프트를 입력합니다.

모호한 프롬프트
교육 안내문 써줘

결과를 확인합니다. 어떤 교육인지, 누구 대상인지, 언제인지 불명확한 결과가 나올 것입니다.

Step 2. 이번에는 아래 구조화된 프롬프트를 입력합니다.

구조화된 프롬프트
너는 대기업 인사팀 사원이야.
다음 주 화요일에 진행하는 사내 엑셀 교육에 대한 안내문을 작성해줘.

- 교육명: 엑셀 데이터 분석 실무
- 일시: 2026년 3월 24일(화) 14:00~17:00
- 장소: 본사 8층 대회의실
- 대상: 전 직원 (선착순 30명)
- 신청 방법: 사내 인트라넷 > 교육 신청
- 마감: 3월 20일(금)
- 톤: 공식적이지만 딱딱하지 않게
- 분량: 300자 내외

비교 포인트: 두 결과의 구체성, 정확성, 활용 가능성을 비교합니다.

실습 2: 맥락 추가하여 결과 개선

실습 1에서 생성한 안내문에 추가 맥락을 제공하여 결과를 개선해봅니다. 반복 수정 방식을 활용하세요.

실습 3: One-shot, Few-shot 프롬프트 작성

직원 피드백 분류 작업을 Zero-shot, One-shot, Few-shot 방식으로 각각 수행하고 결과를 비교합니다.

실습 과제

다음 3개의 문서를 구조화된 프롬프트로 작성하세요.

  1. 사내 교육 안내문 (역할, 대상, 일시, 장소, 톤, 분량 등을 명시)
  2. HR 데이터 분석 보고서 목차 (역할 설정 + 마크다운 제목 구조 활용)
  3. 직원 피드백 분류 (Few-shot 기법 활용, 3개 이상의 예시 포함)

정리

핵심 개념요약
프롬프트 구조지시문(무엇을) + 맥락(배경 정보)
구조화 도구하이픈(-), 마크다운 제목(#), 번호(1,2,3)
Few-shot예시를 제공하여 출력 형식 통제
반복 수정한 번에 완벽을 기대하지 말고 점진적으로 개선
자동화 연결프롬프트 → 함수 생성 → VBA 코드 → 파일 자동화
다음 차시 예고: 2차시에서는 ChatGPT를 활용하여 엑셀 함수와 VBA 코드를 생성하고, HR 데이터(1,470명)를 직접 분석하는 실습을 진행합니다.

2차시. 엑셀 데이터 처리 자동화

학습 목표

  • ChatGPT에 자연어로 엑셀 작업을 요청하는 방법을 익힌다.
  • COUNTIF, AVERAGEIF, UNIQUE 등 핵심 함수를 활용할 수 있다.
  • 조건부 서식, 필터, 정렬 등 데이터 처리 기능을 적용할 수 있다.
  • VBA를 활용하여 함수 입력과 데이터 처리를 자동화할 수 있다.

사용 데이터

  • 파일 위치: 실습파일/HR데이터.xlsx (VBA 포함: 실습파일/HR데이터(VBA포함).xlsm)
  • 규모: 1,470명, 35개 컬럼
  • 주요 컬럼: 나이, 퇴사여부, 출장빈도, 일급, 부서, 통근거리, 학력, 전공분야, 직원수, 사번, 환경만족도, 성별, 시급, 직무몰입도, 직급, 직무, 직무만족도, 결혼상태, 월급, 월급비율, 이전직장수, 성인여부, 초과근무, 급여인상률, 성과등급, 관계만족도, 기본근무시간, 스톡옵션등급, 총경력연수, 작년교육횟수, 워라밸, 근속연수, 현직무연수, 최근승진후연수, 현관리자근무연수
  • 부서(3개): 영업, 연구개발, 인사
  • 직무(9개): 영업임원, 연구원, 실험기술자, 제조이사, 의료담당자, 관리자, 영업사원, 연구이사, 인사담당

1. ChatGPT를 활용한 엑셀 작업 자동화

1.1 엑셀 함수를 자연어로 요청하기

엑셀 함수를 외울 필요가 없습니다. ChatGPT에 원하는 작업을 자연어로 설명하면 함수를 생성해줍니다.

프롬프트 작성 팁

요소설명예시
데이터 위치어떤 열/셀에 데이터가 있는지"O열에 직급 데이터가 있어"
원하는 결과무엇을 구하고 싶은지"직급별 인원 수를 구하고 싶어"
조건필터 조건이 있는지"직급이 1인 사람만"
출력 위치결과를 어디에 넣을지"AJ열에 결과를 넣어줘"

1.2 주요 엑셀 함수

함수용도구문
COUNTIF조건에 맞는 셀 개수=COUNTIF(범위, 조건)
AVERAGEIF조건에 맞는 평균=AVERAGEIF(조건범위, 조건, 평균범위)
UNIQUE고유값 추출=UNIQUE(범위)
COUNTA비어있지 않은 셀 개수=COUNTA(범위)
IF조건 판단=IF(조건, 참값, 거짓값)

1.3 유용한 엑셀 단축키

단축키기능
Ctrl + Shift + L필터 토글
Alt + F11VBA 편집기 열기
Alt + Tab창 전환
Ctrl + Home셀 A1로 이동
Ctrl + End데이터 마지막 셀로 이동
Ctrl + Shift + End현재 셀부터 마지막까지 선택

2. 실습: 엑셀 함수 활용

실습 1: 직급별 인원 수 구하기

HR데이터.xlsx의 O열(직급)에는 1~5의 값이 있습니다. 각 직급별 인원 수를 구합니다.

ChatGPT에 이렇게 요청하세요.

ChatGPT 프롬프트
엑셀에서 O열에 직급 데이터(1~5)가 있어.
AJ1에 "직급", AK1에 "인원수" 헤더를 넣고,
AJ2:AJ6에 직급 번호 1~5를 넣고,
AK2:AK6에 각 직급별 인원 수를 COUNTIF로 구하는 수식을 알려줘.

데이터는 2행부터 1471행까지 있어.

결과 수식 예시

  • AK2 셀: =COUNTIF($O$2:$O$1471, AJ2)
  • AK3~AK6: 같은 수식을 아래로 복사

실습 2: 부서별 평균 월급 구하기

E열(부서)과 S열(월급)을 사용하여 부서별 평균 월급을 구합니다.

ChatGPT에 이렇게 요청하세요.

ChatGPT 프롬프트
엑셀에서 E열에 부서(영업, 연구개발, 인사), S열에 월급 데이터가 있어.
AM1에 "부서", AN1에 "평균월급" 헤더를 넣고,
AM2에 "영업", AM3에 "연구개발", AM4에 "인사"를 넣고,
AN2:AN4에 각 부서별 평균 월급을 AVERAGEIF로 구하는 수식을 알려줘.

데이터는 2행부터 1471행까지 있어.

결과 수식 예시

  • AN2 셀: =AVERAGEIF($E$2:$E$1471, AM2, $S$2:$S$1471)

실습 3: UNIQUE 함수로 부서 목록 추출

ChatGPT에 이렇게 요청하세요.

ChatGPT 프롬프트
엑셀에서 E열에 부서 데이터가 있어.
AP1에 "고유부서목록" 헤더를 넣고,
AP2에 UNIQUE 함수로 고유한 부서 목록을 추출하는 수식을 알려줘.

결과 수식

  • AP2 셀: =UNIQUE(E2:E1471)
참고: UNIQUE 함수는 Microsoft 365 또는 Excel 2021 이상에서 사용 가능합니다.

실습 4: 조건부 서식 적용

직급이 1인 행을 주황색으로 강조합니다.

ChatGPT에 이렇게 요청하세요.

ChatGPT 프롬프트
엑셀에서 O열에 직급 데이터가 있어.
직급이 1인 행 전체를 주황색 배경으로 강조하는 조건부 서식을 설정하는 방법을 알려줘.

수동 설정 방법

  1. 데이터 범위(A2:AI1471)를 선택
  2. 홈 탭 > 조건부 서식 > 새 규칙
  3. "수식을 사용하여 서식을 지정할 셀 결정" 선택
  4. 수식 입력: =$O2=1
  5. 서식 > 채우기 > 주황색 선택 > 확인

실습 5: 필터링

부서가 "영업"이고 월급이 5,000 이상인 직원을 필터링합니다.

ChatGPT에 이렇게 요청하세요.

ChatGPT 프롬프트
엑셀에서 다음 조건으로 필터링하는 방법을 알려줘.
- E열(부서) = "영업"
- S열(월급) >= 5000

수동 설정 방법

  1. Ctrl + Shift + L로 필터 활성화
  2. E열 필터 클릭 > "영업"만 선택
  3. S열 필터 클릭 > 숫자 필터 > 크거나 같음 > 5000 입력

실습 6: 새 열 추가

6-1. 평가등급텍스트 열

성과등급(Y열) 값을 텍스트로 변환하는 열을 추가합니다.

성과등급텍스트
1미흡
2보통
3우수
4탁월

ChatGPT에 이렇게 요청하세요.

ChatGPT 프롬프트
엑셀에서 Y열에 성과등급(1~4)이 있어.
AJ1에 "평가등급텍스트" 헤더를 넣고,
AJ2에 IF 함수로 1=미흡, 2=보통, 3=우수, 4=탁월로 변환하는 수식을 알려줘.

결과 수식

  • AJ2 셀: =IF(Y2=1,"미흡",IF(Y2=2,"보통",IF(Y2=3,"우수",IF(Y2=4,"탁월",""))))

6-2. 관리대상여부 열

다음 조건을 모두 만족하면 "관리대상"으로 표시합니다.

  • 직무만족도(Q열) <= 2
  • 환경만족도(K열) <= 2
  • 워라밸(AE열) <= 2

ChatGPT에 이렇게 요청하세요.

ChatGPT 프롬프트
엑셀에서 다음 조건을 모두 만족하면 "관리대상", 아니면 공백을 반환하는 수식을 알려줘.
- Q열(직무만족도) <= 2
- K열(환경만족도) <= 2
- AE열(워라밸) <= 2

AK1에 "관리대상여부" 헤더를 넣고, AK2에 수식을 넣을 거야.

결과 수식

  • AK2 셀: =IF(AND(Q2<=2, K2<=2, AE2<=2), "관리대상", "")

3. VBA로 자동화하기

위 실습들을 VBA 매크로로 자동화할 수 있습니다. 각 매크로는 ChatGPT에 요청하여 생성한 코드입니다.

VBA 실행 방법: Alt + F11로 VBA 편집기 열기 > 삽입 > 모듈 > 코드 붙여넣기 > F5로 실행
참고: HR데이터(VBA포함).xlsm 파일에는 매크로별로 개별 모듈이 이미 구성되어 있습니다. 직접 작성할 때도 매크로 하나당 모듈 하나씩 분리하는 것을 권장합니다.
모듈명기능차시
M01_직급별인원수직급별 인원 수 집계2차시
M02_부서별평균월급부서별 평균 월급 계산2차시
M03_피벗테이블부서-직무 피벗테이블 생성2차시
M04_차트생성집계 결과 차트 생성2차시
M05_부서별파일분리부서별 엑셀 파일 분리 저장3차시
M06_부서별파일취합분리된 파일 하나로 취합3차시
M07_수료증슬라이드생성PPT 수료증 슬라이드 일괄 생성4차시
M08_수료증PDF생성개인별 수료증 PDF 저장4차시
M09_워드보고서생성워드 문서 자동 생성5차시
M10_이메일발송이메일 자동 발송6차시
M11_엑셀보고서생성HR 데이터 엑셀 보고서 생성7차시
M12_PPT보고서생성HR 데이터 PPT 보고서 생성7차시

매크로 1: 직급별 인원 수

ChatGPT에 이렇게 요청하세요.

ChatGPT 프롬프트
엑셀 VBA 매크로를 작성해줘.

# 데이터
- 시트명: Sheet1
- O열: 직급 (1~5), 2행~1471행

# 작업
- AJ1에 "직급", AK1에 "인원수" 헤더 입력
- AJ2:AJ6에 직급 번호 1~5 입력
- AK2:AK6에 각 직급별 인원 수를 COUNTIF 수식으로 입력
- 완료 후 MsgBox로 완료 메시지 표시

# 주의
- 한글은 ChrW() 함수를 사용할 것

VBA 코드

VBA - 직급별인원수
Sub 직급별인원수()
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("Sheet1")

    ' 헤더 입력
    ws.Range("AJ1").Value = ChrW(51649) & ChrW(44553)  ' 직급
    ws.Range("AK1").Value = ChrW(51064) & ChrW(50896) & ChrW(49688)  ' 인원수

    ' 직급 번호 입력
    Dim i As Long
    For i = 1 To 5
        ws.Cells(i + 1, 36).Value = i  ' AJ열 = 36번째
    Next i

    ' COUNTIF 수식 입력
    For i = 2 To 6
        ws.Cells(i, 37).Formula = "=COUNTIF($O$2:$O$1471,AJ" & i & ")"  ' AK열 = 37번째
    Next i

    MsgBox ChrW(51649) & ChrW(44553) & ChrW(48324) & " " & ChrW(51064) & ChrW(50896) & ChrW(49688) & " " & ChrW(44228) & ChrW(49328) & ChrW(51060) & " " & ChrW(50756) & ChrW(47308) & ChrW(46104) & ChrW(50632) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
    ' "직급별 인원수 계산이 완료되었습니다."
End Sub

실행 결과

직급별 인원수 실행 결과

매크로 2: 부서별 평균 월급

ChatGPT에 이렇게 요청하세요.

ChatGPT 프롬프트
엑셀 VBA 매크로를 작성해줘.

# 데이터
- 시트명: Sheet1
- E열: 부서 (영업, 연구개발, 인사), 2행~1471행
- S열: 월급

# 작업
- AM1에 "부서", AN1에 "평균월급" 헤더 입력
- AM2에 "영업", AM3에 "연구개발", AM4에 "인사" 입력
- AN2:AN4에 각 부서별 평균 월급을 AVERAGEIF 수식으로 입력
- 완료 후 MsgBox로 완료 메시지 표시

# 주의
- 한글은 ChrW() 함수를 사용할 것

VBA 코드

VBA - 부서별평균월급
Sub 부서별평균월급()
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("Sheet1")

    ' 헤더 입력
    ws.Range("AM1").Value = ChrW(48512) & ChrW(49436)  ' 부서
    ws.Range("AN1").Value = ChrW(54217) & ChrW(44512) & ChrW(50900) & ChrW(44553)  ' 평균월급

    ' 부서명 입력
    ws.Range("AM2").Value = ChrW(50689) & ChrW(50629)  ' 영업
    ws.Range("AM3").Value = ChrW(50672) & ChrW(44396) & ChrW(44060) & ChrW(48156)  ' 연구개발
    ws.Range("AM4").Value = ChrW(51064) & ChrW(49324)  ' 인사

    ' AVERAGEIF 수식 입력
    Dim i As Long
    For i = 2 To 4
        ws.Cells(i, 40).Formula = "=AVERAGEIF($E$2:$E$1471,AM" & i & ",$S$2:$S$1471)"  ' AN열 = 40번째
    Next i

    MsgBox ChrW(48512) & ChrW(49436) & ChrW(48324) & " " & ChrW(54217) & ChrW(44512) & ChrW(50900) & ChrW(44553) & " " & ChrW(44228) & ChrW(49328) & ChrW(51060) & " " & ChrW(50756) & ChrW(47308) & ChrW(46104) & ChrW(50632) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
    ' "부서별 평균월급 계산이 완료되었습니다."
End Sub

실행 결과

부서별 평균 월급 실행 결과

매크로 3: 고유 부서 목록

ChatGPT에 이렇게 요청하세요.

ChatGPT 프롬프트
엑셀 VBA 매크로를 작성해줘.

# 데이터
- 시트명: Sheet1
- E열: 부서, 2행~1471행

# 작업
- AP1에 "고유부서목록" 헤더 입력
- E열에서 고유한 부서 값을 추출하여 AP2부터 아래로 입력
- Collection 또는 Dictionary를 사용하여 중복 제거
- 완료 후 MsgBox로 완료 메시지 표시

# 주의
- 한글은 ChrW() 함수를 사용할 것

VBA 코드

VBA - 고유부서목록
Sub 고유부서목록()
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("Sheet1")

    ' 헤더 입력
    ws.Range("AP1").Value = ChrW(44256) & ChrW(50976) & ChrW(48512) & ChrW(49436) & ChrW(47785) & ChrW(47197)  ' 고유부서목록

    ' Collection으로 고유값 추출
    Dim col As New Collection
    Dim cell As Range
    Dim val As String

    On Error Resume Next
    For Each cell In ws.Range("E2:E1471")
        val = cell.Value
        If val <> "" Then
            col.Add val, val
        End If
    Next cell
    On Error GoTo 0

    ' 고유값 입력
    Dim i As Long
    For i = 1 To col.Count
        ws.Cells(i + 1, 42).Value = col(i)  ' AP열 = 42번째
    Next i

    MsgBox ChrW(44256) & ChrW(50976) & " " & ChrW(48512) & ChrW(49436) & " " & ChrW(47785) & ChrW(47197) & ChrW(51060) & " " & ChrW(52628) & ChrW(52636) & ChrW(46104) & ChrW(50632) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
    ' "고유 부서 목록이 추출되었습니다."
End Sub

실행 결과

고유 부서 목록 실행 결과

매크로 4: 조건부 서식 적용

ChatGPT에 이렇게 요청하세요.

ChatGPT 프롬프트
엑셀 VBA 매크로를 작성해줘.

# 데이터
- 시트명: Sheet1
- O열: 직급, 2행~1471행
- 데이터 범위: A2:AI1471

# 작업
- O열(직급)이 1인 행 전체에 주황색 배경 조건부 서식 적용
- 조건부 서식 수식: =$O2=1
- 배경색: 주황색 (RGB 255, 165, 0)
- 완료 후 MsgBox로 완료 메시지 표시

# 주의
- 한글은 ChrW() 함수를 사용할 것

VBA 코드

VBA - 조건부서식적용
Sub 조건부서식적용()
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("Sheet1")

    ' 데이터 범위 설정
    Dim rng As Range
    Set rng = ws.Range("A2:AI1471")

    ' 기존 조건부 서식 제거 (선택)
    rng.FormatConditions.Delete

    ' 조건부 서식 추가: O열 = 1이면 주황색 배경
    Dim fc As FormatCondition
    Set fc = rng.FormatConditions.Add(Type:=xlExpression, Formula1:="=$O2=1")
    fc.Interior.Color = RGB(255, 165, 0)
    fc.StopIfTrue = False

    MsgBox ChrW(51649) & ChrW(44553) & "=1 " & ChrW(54665) & ChrW(50640) & " " & ChrW(51452) & ChrW(54889) & ChrW(49353) & " " & ChrW(48176) & ChrW(44221) & ChrW(49353) & ChrW(51060) & " " & ChrW(51201) & ChrW(50857) & ChrW(46104) & ChrW(50632) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
    ' "직급=1 행에 주황색 배경색이 적용되었습니다."
End Sub

실행 결과

조건부 서식 적용 실행 결과

매크로 5: 필터링 실습

ChatGPT에 이렇게 요청하세요.

ChatGPT 프롬프트
엑셀 VBA 매크로를 작성해줘.

# 데이터
- 시트명: Sheet1
- E열(5번째): 부서
- S열(19번째): 월급
- 데이터 범위: A1:AI1471 (1행은 헤더)

# 작업
- E열 = "영업" AND S열 >= 5000 조건으로 AutoFilter 적용
- 완료 후 MsgBox로 완료 메시지 표시

# 주의
- 한글은 ChrW() 함수를 사용할 것

VBA 코드

VBA - 필터링실습
Sub 필터링실습()
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("Sheet1")

    ' 기존 필터 해제
    If ws.AutoFilterMode Then ws.AutoFilterMode = False

    ' AutoFilter 적용
    Dim rng As Range
    Set rng = ws.Range("A1:AI1471")

    rng.AutoFilter Field:=5, Criteria1:=ChrW(50689) & ChrW(50629)  ' 부서 = "영업"
    rng.AutoFilter Field:=19, Criteria1:=">=5000"  ' 월급 >= 5000

    MsgBox ChrW(48512) & ChrW(49436) & "=" & ChrW(50689) & ChrW(50629) & ", " & ChrW(50900) & ChrW(44553) & ">=5000 " & ChrW(54596) & ChrW(53552) & ChrW(47553) & ChrW(51060) & " " & ChrW(51201) & ChrW(50857) & ChrW(46104) & ChrW(50632) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
    ' "부서=영업, 월급>=5000 필터링이 적용되었습니다."
End Sub

실행 결과

필터링 실행 결과

매크로 6: 평가등급 열 추가

ChatGPT에 이렇게 요청하세요.

ChatGPT 프롬프트
엑셀 VBA 매크로를 작성해줘.

# 데이터
- 시트명: Sheet1
- Y열(25번째): 성과등급 (1~4), 2행~1471행

# 작업
- AJ1에 "평가등급텍스트" 헤더 입력
- AJ2:AJ1471에 IF 수식 입력: 1=미흡, 2=보통, 3=우수, 4=탁월
- 완료 후 MsgBox로 완료 메시지 표시

# 주의
- 한글은 ChrW() 함수를 사용할 것
- 수식 내 한글도 ChrW()로 처리

VBA 코드

VBA - 평가등급열추가
Sub 평가등급열추가()
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("Sheet1")

    ' 헤더 입력
    ws.Range("AJ1").Value = ChrW(54217) & ChrW(44032) & ChrW(46321) & ChrW(44553) & ChrW(53581) & ChrW(49828) & ChrW(53944)  ' 평가등급텍스트

    ' IF 수식 입력
    Dim i As Long
    Dim formula As String

    ' 한글 텍스트를 변수에 저장
    Dim txt1 As String, txt2 As String, txt3 As String, txt4 As String
    txt1 = ChrW(48120) & ChrW(55121)  ' 미흡
    txt2 = ChrW(48372) & ChrW(53685)  ' 보통
    txt3 = ChrW(50864) & ChrW(49688)  ' 우수
    txt4 = ChrW(53441) & ChrW(50900)  ' 탁월

    For i = 2 To 1471
        ws.Cells(i, 36).Value = ""
        Select Case ws.Cells(i, 25).Value
            Case 1: ws.Cells(i, 36).Value = txt1
            Case 2: ws.Cells(i, 36).Value = txt2
            Case 3: ws.Cells(i, 36).Value = txt3
            Case 4: ws.Cells(i, 36).Value = txt4
        End Select
    Next i

    MsgBox ChrW(54217) & ChrW(44032) & ChrW(46321) & ChrW(44553) & ChrW(53581) & ChrW(49828) & ChrW(53944) & " " & ChrW(50676) & ChrW(51060) & " " & ChrW(52628) & ChrW(44032) & ChrW(46104) & ChrW(50632) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
    ' "평가등급텍스트 열이 추가되었습니다."
End Sub

실행 결과

평가등급 열 추가 실행 결과

매크로 7: 관리대상 여부 열 추가

ChatGPT에 이렇게 요청하세요.

ChatGPT 프롬프트
엑셀 VBA 매크로를 작성해줘.

# 데이터
- 시트명: Sheet1
- Q열(17번째): 직무만족도, 2행~1471행
- K열(11번째): 환경만족도
- AE열(31번째): 워라밸

# 작업
- AK1에 "관리대상여부" 헤더 입력
- AK2:AK1471에 값 입력
  - 직무만족도 <= 2 AND 환경만족도 <= 2 AND 워라밸 <= 2 이면 "관리대상"
  - 아니면 빈 문자열
- 완료 후 MsgBox로 완료 메시지 표시

# 주의
- 한글은 ChrW() 함수를 사용할 것

VBA 코드

VBA - 관리대상여부열추가
Sub 관리대상여부열추가()
    Dim ws As Worksheet
    Set ws = ThisWorkbook.Sheets("Sheet1")

    ' 헤더 입력
    ws.Range("AK1").Value = ChrW(44288) & ChrW(47532) & ChrW(45824) & ChrW(49345) & ChrW(50668) & ChrW(48512)  ' 관리대상여부

    ' 관리대상 판별
    Dim i As Long
    Dim txtTarget As String
    txtTarget = ChrW(44288) & ChrW(47532) & ChrW(45824) & ChrW(49345)  ' 관리대상

    For i = 2 To 1471
        If ws.Cells(i, 17).Value <= 2 And ws.Cells(i, 11).Value <= 2 And ws.Cells(i, 31).Value <= 2 Then
            ws.Cells(i, 37).Value = txtTarget
        Else
            ws.Cells(i, 37).Value = ""
        End If
    Next i

    MsgBox ChrW(44288) & ChrW(47532) & ChrW(45824) & ChrW(49345) & ChrW(50668) & ChrW(48512) & " " & ChrW(50676) & ChrW(51060) & " " & ChrW(52628) & ChrW(44032) & ChrW(46104) & ChrW(50632) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
    ' "관리대상여부 열이 추가되었습니다."
End Sub

실행 결과

관리대상 여부 열 추가 실행 결과

4. 차트 생성 (보너스)

ChatGPT에 이렇게 요청하세요.

ChatGPT 프롬프트
엑셀 VBA 매크로를 작성해줘.

# 데이터
- 시트명: Sheet1
- AJ1:AK6에 직급별 인원수 데이터가 있음 (AJ=직급, AK=인원수)

# 작업
- 직급별 인원수 막대 차트를 생성
- 차트 제목: "직급별 인원 수"
- 차트를 새 시트가 아닌 같은 시트에 삽입
- 완료 후 MsgBox로 완료 메시지 표시

# 주의
- 한글은 ChrW() 함수를 사용할 것

실행 결과

차트 생성 실행 결과

정리

핵심 내용요약
자연어 요청ChatGPT에 데이터 위치, 원하는 결과, 조건을 설명하면 함수 생성
핵심 함수COUNTIF, AVERAGEIF, UNIQUE, IF, AND
조건부 서식수식 기반으로 특정 조건의 행 강조
필터링AutoFilter로 다중 조건 필터 적용
VBA 자동화반복 작업을 매크로로 한 번에 실행
ChrW()VBA에서 한글 문자열 처리 방법
다음 차시 예고: 3차시에서는 VBA를 활용하여 부서별로 파일을 분리하고, 분리된 파일을 다시 취합하는 파일 단위 자동화를 실습합니다.

3차시. 엑셀 파일 분리 및 취합 자동화

학습 목표

  • VBA의 개념과 역할을 이해한다.
  • VBA 편집기에서 모듈을 삽입하고 코드를 실행할 수 있다.
  • ChatGPT에 VBA 코드를 효과적으로 요청하는 프롬프트를 작성할 수 있다.
  • 부서별로 데이터를 분리하여 개별 파일로 저장할 수 있다.
  • 분리된 파일을 하나의 파일로 취합할 수 있다.
  • .xlsm 저장과 .bas 내보내기를 수행할 수 있다.

사용 데이터

  • 파일 위치: 실습파일/HR데이터.xlsx
  • 규모: 1,470명, 35개 컬럼
  • 부서(3개): 영업, 연구개발, 인사
  • 분리 기준: E열(부서) 기준으로 3개 파일 생성
  • 결과 저장 위치: 자동화실행결과/부서별분리/

1. VBA 개념과 역할

1.1 VBA란?

VBA(Visual Basic for Applications)는 Microsoft Office에 내장된 프로그래밍 언어입니다. 엑셀에서 반복적인 작업을 자동화하는 데 사용합니다.

항목설명
정식 명칭Visual Basic for Applications
용도엑셀 작업 자동화 (매크로)
실행 환경엑셀 내장 VBA 편집기
파일 형식.xlsm (매크로 포함 통합 문서)

1.2 VBA로 할 수 있는 일

  • 셀에 값·수식 자동 입력
  • 조건부 서식, 필터 자동 적용
  • 파일 분리: 조건별로 데이터를 나누어 개별 파일로 저장
  • 파일 취합: 여러 파일의 데이터를 하나로 합치기
  • 차트 자동 생성
  • 반복 보고서 자동 생성

1.3 왜 ChatGPT와 함께 사용하는가?

VBA 문법을 몰라도 ChatGPT에 원하는 작업을 자연어로 설명하면 코드를 생성해줍니다.

ChatGPT + VBA 워크플로우
"나는 VBA를 모르지만, 원하는 작업을 설명할 수 있다"
→ ChatGPT가 코드를 작성해준다
→ 복사하여 실행하면 된다

2. VBA 편집기 사용법

2.1 VBA 편집기 열기

  1. 엑셀에서 Alt + F11 을 누릅니다.
  2. VBA 편집기 창이 열립니다.
VBA 편집기 화면

2.2 모듈 삽입

  1. VBA 편집기 상단 메뉴에서 삽입 > 모듈 을 클릭합니다.
  2. 오른쪽에 빈 코드 창이 나타납니다.
  3. 이 창에 VBA 코드를 붙여넣습니다.
참고: 매크로별 모듈 분리
실습 파일 HR데이터(VBA포함).xlsm에는 매크로가 기능별로 개별 모듈에 들어 있습니다. 예: M09_부서별파일분리, M10_파일취합
직접 만들 때도 매크로 하나당 모듈 하나씩 분리하면 관리가 편합니다.

2.3 코드 실행

방법설명
F5커서가 있는 매크로 실행
F8한 줄씩 실행 (디버깅)
실행 버튼툴바의 초록색 재생 버튼 클릭
매크로 대화상자엑셀에서 Alt + F8 > 매크로 선택 > 실행

2.4 코드 실행 전 체크리스트

  • HR데이터.xlsx 파일이 열려 있는가?
  • 시트명이 "Sheet1"인가?
  • 데이터가 1행(헤더) + 2~1471행(데이터)으로 되어 있는가?
  • 코드를 올바른 모듈에 붙여넣었는가?

3. ChatGPT에 VBA 코드 요청하는 프롬프트 작성법

3.1 프롬프트 구조

VBA 코드를 요청할 때는 다음 4가지를 명확히 전달합니다.

VBA 요청 프롬프트 구조
# 데이터 정보
- 파일명, 시트명
- 데이터 범위 (행, 열)
- 컬럼 설명

# 원하는 작업
- 구체적인 동작 설명
- 순서대로 나열

# 출력 결과
- 파일 저장 위치, 파일명 규칙
- 완료 메시지

# 주의사항
- 한글 처리 (ChrW 사용)
- 오류 처리
- 기타 제약 조건

3.2 좋은 프롬프트 예시

좋은 VBA 요청 프롬프트
엑셀 VBA 매크로를 작성해줘.

# 데이터
- 파일명: HR데이터.xlsx
- 시트명: Sheet1
- 데이터 범위: A1:AI1471 (1행은 헤더, 2~1471행은 데이터)
- E열: 부서 (영업, 연구개발, 인사)

# 작업
- E열(부서)의 고유값을 기준으로 데이터를 분리
- 각 부서별로 새 워크북을 생성
- 헤더(1행)를 각 파일에 포함
- 해당 부서의 데이터만 복사
- 원본 파일과 같은 폴더에 "부서명.xlsx"로 저장
- 저장 후 워크북 닫기
- 완료 후 MsgBox로 완료 메시지 표시

# 주의
- 한글은 ChrW() 함수를 사용할 것
- 이미 같은 이름의 파일이 있으면 덮어쓰기
- Application.ScreenUpdating을 False로 설정하여 속도 향상

3.3 나쁜 프롬프트 예시

나쁜 VBA 요청 프롬프트
엑셀 파일 분리하는 VBA 코드 써줘
이 프롬프트는 데이터 위치, 분리 기준, 파일명 규칙, 저장 위치 등 핵심 정보가 빠져 있어 원하는 결과를 얻기 어렵습니다.

4. 파일 분리 설계

4.1 파일 분리란?

하나의 큰 데이터 파일을 특정 기준(부서, 지역, 날짜 등)으로 나누어 개별 파일로 저장하는 작업입니다.

파일 분리 구조
HR데이터.xlsx (1,470명)
  ├── 영업.xlsx (446명)
  ├── 연구개발.xlsx (961명)
  └── 인사.xlsx (63명)

4.2 설계 시 결정사항

항목결정할 내용이 실습의 설정
분리 기준어떤 컬럼으로 나눌 것인가E열(부서)
고유값몇 개로 나뉘는가3개 (영업, 연구개발, 인사)
파일명 규칙저장할 파일명 형식"부서명.xlsx"
저장 위치어디에 저장할 것인가자동화실행결과/부서별분리/ 폴더
헤더 포함각 파일에 헤더를 넣을 것인가
파일 형식xlsx, csv 등.xlsx

5. 실습: 부서별 파일 분리

5.1 VBA 코드 요청 프롬프트

ChatGPT에 이렇게 요청하세요.

ChatGPT 프롬프트 - 파일 분리
엑셀 VBA 매크로를 작성해줘.

# 데이터
- 현재 열려 있는 워크북의 Sheet1 시트
- A1:AI1471 (1행: 헤더, 2~1471행: 데이터, 35개 컬럼)
- E열(5번째 컬럼): 부서 (영업, 연구개발, 인사)

# 작업
1. E열에서 고유한 부서 값을 추출
2. 각 부서별로 새 워크북 생성
3. 헤더(1행)를 새 워크북에 복사
4. 해당 부서의 데이터 행만 새 워크북에 복사
5. `자동화실행결과/부서별분리/` 폴더에 "부서명.xlsx"로 저장
6. 저장 후 새 워크북 닫기
7. 모든 부서 처리 후 MsgBox로 "부서별 파일 분리가 완료되었습니다." 메시지 표시

# 주의
- 한글은 ChrW() 함수를 사용할 것
- Application.ScreenUpdating = False로 속도 향상
- Application.DisplayAlerts = False로 덮어쓰기 경고 무시
- 작업 완료 후 ScreenUpdating, DisplayAlerts를 True로 복원

5.2 VBA 코드

VBA - 부서별파일분리
Sub 부서별파일분리()
    Dim wsSource As Worksheet
    Set wsSource = ThisWorkbook.Sheets("Sheet1")

    Dim savePath As String
    ' 결과를 자동화실행결과\부서별분리 폴더에 저장
    Dim resultBase As String
    resultBase = ThisWorkbook.Path & "\..\자동화실행결과\"
    If Dir(resultBase, vbDirectory) = "" Then MkDir resultBase
    savePath = resultBase & "부서별분리\"
    If Dir(savePath, vbDirectory) = "" Then MkDir savePath

    ' 속도 향상 설정
    Application.ScreenUpdating = False
    Application.DisplayAlerts = False

    ' E열에서 고유 부서 추출
    Dim deptCol As New Collection
    Dim cell As Range
    Dim deptName As String

    On Error Resume Next
    For Each cell In wsSource.Range("E2:E1471")
        deptName = cell.Value
        If deptName <> "" Then
            deptCol.Add deptName, deptName
        End If
    Next cell
    On Error GoTo 0

    ' 데이터 범위 설정
    Dim lastCol As Long
    lastCol = wsSource.Cells(1, wsSource.Columns.Count).End(xlToLeft).Column

    ' 각 부서별 파일 생성
    Dim d As Long
    For d = 1 To deptCol.Count
        deptName = deptCol(d)

        ' 새 워크북 생성
        Dim wbNew As Workbook
        Set wbNew = Workbooks.Add(xlWBATWorksheet)
        Dim wsNew As Worksheet
        Set wsNew = wbNew.Sheets(1)

        ' 헤더 복사
        wsSource.Range(wsSource.Cells(1, 1), wsSource.Cells(1, lastCol)).Copy wsNew.Range("A1")

        ' 해당 부서 데이터 복사
        Dim targetRow As Long
        targetRow = 2
        Dim i As Long
        For i = 2 To 1471
            If wsSource.Cells(i, 5).Value = deptName Then
                wsSource.Range(wsSource.Cells(i, 1), wsSource.Cells(i, lastCol)).Copy wsNew.Cells(targetRow, 1)
                targetRow = targetRow + 1
            End If
        Next i

        ' 열 너비 자동 맞춤
        wsNew.Cells.EntireColumn.AutoFit

        ' 파일 저장 및 닫기
        wbNew.SaveAs savePath & deptName & ".xlsx", xlOpenXMLWorkbook
        wbNew.Close SaveChanges:=False
    Next d

    ' 설정 복원
    Application.ScreenUpdating = True
    Application.DisplayAlerts = True

    MsgBox ChrW(48512) & ChrW(49436) & ChrW(48324) & " " & ChrW(54028) & ChrW(51068) & " " & ChrW(48516) & ChrW(47532) & ChrW(44032) & " " & ChrW(50756) & ChrW(47308) & ChrW(46104) & ChrW(50632) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
    ' "부서별 파일 분리가 완료되었습니다."
End Sub

5.3 실행 결과

부서별 파일 분리 실행 결과

실행 후 자동화실행결과/부서별분리/ 폴더에 다음 3개 파일이 생성됩니다.

파일명내용
영업.xlsx영업 부서 직원 데이터 (446명)
연구개발.xlsx연구개발 부서 직원 데이터 (961명)
인사.xlsx인사 부서 직원 데이터 (63명)

6. 파일 취합 설계

6.1 파일 취합이란?

여러 개의 엑셀 파일에 흩어진 데이터를 하나의 파일로 합치는 작업입니다.

파일 취합 구조
영업.xlsx (446명)
연구개발.xlsx (961명)     →     HR데이터_취합.xlsx (1,470명)
인사.xlsx (63명)

6.2 설계 시 결정사항

항목결정할 내용이 실습의 설정
대상 폴더어느 폴더의 파일을 합칠 것인가자동화실행결과/부서별분리/ 폴더
대상 파일어떤 파일을 합칠 것인가*.xlsx (원본 제외)
헤더 처리첫 파일만 헤더 포함
결과 파일명합친 결과 파일명HR데이터_취합.xlsx
데이터 시트어떤 시트의 데이터를 가져올 것인가각 파일의 첫 번째 시트

7. 실습: 파일 취합

7.1 VBA 코드 요청 프롬프트

ChatGPT에 이렇게 요청하세요.

ChatGPT 프롬프트 - 파일 취합
엑셀 VBA 매크로를 작성해줘.

# 작업 환경
- 현재 열려 있는 워크북에서 실행
- `자동화실행결과/부서별분리/` 폴더에 있는 .xlsx 파일들을 취합

# 작업
1. `자동화실행결과/부서별분리/` 폴더에서 .xlsx 파일 목록을 가져옴
2. 새 워크북을 생성
3. 첫 번째 파일에서 헤더(1행) 포함하여 데이터 복사
4. 나머지 파일에서는 헤더 제외(2행부터) 데이터 복사
5. 모든 파일의 데이터를 순서대로 아래에 이어 붙이기
6. `자동화실행결과/` 폴더에 "HR데이터_취합.xlsx"로 저장
8. 완료 후 MsgBox로 "파일 취합이 완료되었습니다. (총 X건)" 메시지 표시

# 주의
- 한글은 ChrW() 함수를 사용할 것
- Application.ScreenUpdating = False로 속도 향상
- Dir 함수로 파일 목록 순회
- 각 파일을 열고 데이터 복사 후 닫기

7.2 VBA 코드

VBA - 파일취합
Sub 파일취합()
    Dim folderPath As String
    ' 자동화실행결과\부서별분리 폴더에서 읽기
    folderPath = ThisWorkbook.Path & "\..\자동화실행결과\부서별분리\"

    Dim thisFileName As String
    thisFileName = ThisWorkbook.Name

    ' 속도 향상 설정
    Application.ScreenUpdating = False
    Application.DisplayAlerts = False

    ' 새 워크북 생성
    Dim wbResult As Workbook
    Set wbResult = Workbooks.Add(xlWBATWorksheet)
    Dim wsResult As Worksheet
    Set wsResult = wbResult.Sheets(1)

    Dim nextRow As Long
    nextRow = 1
    Dim isFirst As Boolean
    isFirst = True
    Dim totalCount As Long
    totalCount = 0

    ' 폴더 내 xlsx 파일 순회
    Dim fileName As String
    fileName = Dir(folderPath & "*.xlsx")

    Do While fileName <> ""
        ' 현재 파일과 결과 파일 제외
        If fileName <> thisFileName And fileName <> "HR" & ChrW(45936) & ChrW(51060) & ChrW(53552) & "_" & ChrW(52712) & ChrW(54633) & ".xlsx" Then
            ' 파일 열기
            Dim wbSource As Workbook
            Set wbSource = Workbooks.Open(folderPath & fileName, ReadOnly:=True)
            Dim wsSource As Worksheet
            Set wsSource = wbSource.Sheets(1)

            ' 데이터 범위 확인
            Dim lastRow As Long
            Dim lastCol As Long
            lastRow = wsSource.Cells(wsSource.Rows.Count, 1).End(xlUp).Row
            lastCol = wsSource.Cells(1, wsSource.Columns.Count).End(xlToLeft).Column

            If isFirst Then
                ' 첫 파일: 헤더 포함 복사
                wsSource.Range(wsSource.Cells(1, 1), wsSource.Cells(lastRow, lastCol)).Copy wsResult.Cells(nextRow, 1)
                nextRow = nextRow + lastRow
                totalCount = totalCount + (lastRow - 1)
                isFirst = False
            Else
                ' 이후 파일: 헤더 제외 (2행부터)
                If lastRow >= 2 Then
                    wsSource.Range(wsSource.Cells(2, 1), wsSource.Cells(lastRow, lastCol)).Copy wsResult.Cells(nextRow, 1)
                    nextRow = nextRow + (lastRow - 1)
                    totalCount = totalCount + (lastRow - 1)
                End If
            End If

            wbSource.Close SaveChanges:=False
        End If

        fileName = Dir()
    Loop

    ' 열 너비 자동 맞춤
    wsResult.Cells.EntireColumn.AutoFit

    ' 결과 파일 저장
    Dim resultName As String
    resultName = "HR" & ChrW(45936) & ChrW(51060) & ChrW(53552) & "_" & ChrW(52712) & ChrW(54633) & ".xlsx"
    Dim resultPath As String
    resultPath = ThisWorkbook.Path & "\..\자동화실행결과\"
    wbResult.SaveAs resultPath & resultName, xlOpenXMLWorkbook
    wbResult.Close SaveChanges:=False

    ' 설정 복원
    Application.ScreenUpdating = True
    Application.DisplayAlerts = True

    MsgBox ChrW(54028) & ChrW(51068) & " " & ChrW(52712) & ChrW(54633) & ChrW(51060) & " " & ChrW(50756) & ChrW(47308) & ChrW(46104) & ChrW(50632) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "." & vbCrLf & "(" & ChrW(52509) & " " & totalCount & ChrW(44148) & ")"
    ' "파일 취합이 완료되었습니다." & vbCrLf & "(총 X건)"
End Sub

7.3 실행 결과

실행 후 자동화실행결과/ 폴더에 HR데이터_취합.xlsx 파일이 생성되며, 3개 부서 파일의 데이터가 하나로 합쳐집니다.

8. .xlsm 저장과 .bas 내보내기

8.1 .xlsm으로 저장하기

VBA 매크로가 포함된 파일은 반드시 .xlsm 형식으로 저장해야 합니다. 일반 .xlsx로 저장하면 매크로가 삭제됩니다.

저장 방법

  1. 파일 > 다른 이름으로 저장
  2. 파일 형식에서 Excel 매크로 사용 통합 문서 (*.xlsm) 선택
  3. 저장

8.2 .bas로 내보내기

VBA 코드를 .bas 파일로 내보내면 다른 워크북에서 재사용할 수 있습니다.

내보내기 방법

  1. Alt + F11로 VBA 편집기 열기
  2. 왼쪽 프로젝트 탐색기에서 내보낼 모듈을 마우스 오른쪽 클릭
  3. 파일 내보내기 선택
  4. 저장 위치와 파일명 지정 후 저장

가져오기 방법

  1. VBA 편집기에서 파일 > 파일 가져오기
  2. .bas 파일 선택
  3. 모듈이 프로젝트에 추가됨
: .bas 파일은 텍스트 파일이므로 메모장으로도 열어볼 수 있습니다. 팀원과 공유하거나 버전 관리에 활용하세요.

9. 실무 오류와 디버깅

9.1 자주 발생하는 오류

오류원인해결 방법
실행 시간 오류 '9': 첨자 사용이 잘못되었습니다시트명이 코드와 다름시트명 확인 후 코드 수정
실행 시간 오류 '1004'파일 경로 오류 또는 권한 부족경로 확인, 파일이 열려있지 않은지 확인
실행 시간 오류 '13': 형식이 일치하지 않습니다데이터 타입 불일치셀 값의 데이터 타입 확인
매크로를 사용할 수 없습니다매크로 보안 설정파일 > 옵션 > 보안 센터 > 매크로 설정
한글 깨짐ChrW() 미사용한글 문자열을 ChrW()로 변환

9.2 디버깅 방법

F8 단계 실행

  1. VBA 편집기에서 매크로 첫 줄에 커서를 놓습니다.
  2. F8을 눌러 한 줄씩 실행합니다.
  3. 노란색으로 강조된 줄이 현재 실행할 줄입니다.
  4. 변수 위에 마우스를 올리면 현재 값을 확인할 수 있습니다.

중단점 설정

  1. 코드 왼쪽 여백을 클릭하면 빨간 점(중단점)이 생깁니다.
  2. F5로 실행하면 중단점에서 멈춥니다.
  3. F8로 한 줄씩 진행하며 값을 확인합니다.

Debug.Print 활용

VBA - Debug.Print 예시
Debug.Print "현재 부서: " & deptName
Debug.Print "행 번호: " & i

결과는 VBA 편집기의 직접 실행 창(Ctrl + G)에서 확인합니다.

9.3 ChatGPT로 오류 해결하기

오류가 발생하면 ChatGPT에 다음 정보를 전달합니다.

ChatGPT에 오류 전달 프롬프트
엑셀 VBA 매크로 실행 중 오류가 발생했어.

# 오류 메시지
실행 시간 오류 '9': 첨자 사용이 잘못되었습니다.

# 오류 발생 위치
Set ws = ThisWorkbook.Sheets("Sheet1")

# 상황
- 시트 이름이 "데이터"로 되어 있음
- 코드에서는 "Sheet1"을 참조하고 있음

이 오류를 해결해줘.

10. 실습 과제: 전체 프로세스 수행

아래 순서대로 전체 프로세스를 직접 수행해보세요.

단계 1: 환경 준비

  • 실습파일/HR데이터(VBA포함).xlsm 파일 열기 (또는 HR데이터.xlsx를 열고 직접 VBA 작성)
  • Alt + F11로 VBA 편집기 열기
  • 삽입 > 모듈로 새 모듈 추가 (매크로별로 모듈을 분리하는 것을 권장)

단계 2: 파일 분리 실행

  • 부서별파일분리 매크로 코드 붙여넣기
  • F5로 실행
  • 자동화실행결과/부서별분리/ 폴더에 영업.xlsx, 연구개발.xlsx, 인사.xlsx 생성 확인

단계 3: 분리 결과 확인

  • 각 파일을 열어 데이터 확인
  • 헤더가 올바르게 포함되어 있는지 확인
  • 각 파일의 데이터가 해당 부서만 포함하는지 확인

단계 4: 파일 취합 실행

  • 파일취합 매크로 코드 붙여넣기
  • F5로 실행
  • 자동화실행결과/ 폴더에 HR데이터_취합.xlsx 생성 확인
  • 총 데이터 건수가 1,470건인지 확인

단계 5: 파일 저장 및 내보내기

  • HR데이터.xlsx를 HR데이터.xlsm으로 다른 이름으로 저장
  • VBA 편집기에서 모듈을 마우스 오른쪽 클릭 > 파일 내보내기 > .bas로 저장

단계 6: 응용 (선택)

  • E열(부서) 대신 P열(직무)로 분리 기준을 변경해보기
  • ChatGPT에 코드 수정을 요청하여 9개 직무별 파일 분리 수행

정리

핵심 내용요약
VBA 편집기Alt + F11로 열고, 삽입 > 모듈 > 코드 붙여넣기 > F5 실행
프롬프트 작성데이터 정보 + 작업 내용 + 출력 형식 + 주의사항
파일 분리고유값 기준으로 데이터를 나누어 개별 파일로 저장
파일 취합여러 파일의 데이터를 하나의 파일로 합치기
.xlsm 저장매크로 포함 파일은 반드시 .xlsm으로 저장
.bas 내보내기VBA 코드를 파일로 내보내 재사용
디버깅F8 단계 실행, Debug.Print, ChatGPT에 오류 문의
교육 마무리: 1차시 프롬프트 엔지니어링 → 2차시 엑셀 함수·VBA 자동화 → 3차시 파일 분리·취합까지, ChatGPT와 함께하는 엑셀 업무 자동화의 전체 흐름을 학습했습니다. 실무에서 반복되는 작업이 있다면 ChatGPT에 프롬프트를 작성하여 자동화해보세요.

4차시. 파워포인트 자동화

학습 목표

  • VBA가 엑셀뿐 아니라 파워포인트에서도 동일한 원리로 동작함을 이해한다
  • 템플릿 기반 문서 생성(플레이스홀더 치환) 방식을 익힌다
  • 엑셀 데이터와 PPT 템플릿을 연결하여 반복 슬라이드를 자동 생성한다
  • 생성된 슬라이드를 개별 PDF로 저장하는 자동화를 구현한다

1. VBA는 PPT에서도 동작한다

엑셀에서 사용한 VBA와 동일한 방식으로 파워포인트에서도 매크로를 작성하고 실행할 수 있습니다. 파워포인트 VBA 편집기도 Alt + F11로 열 수 있으며, 모듈을 삽입하고 코드를 붙여 넣는 과정이 엑셀과 같습니다.

파워포인트 VBA 편집기 여는 방법

  1. 파워포인트를 열고 Alt + F11을 누른다
  2. [삽입] → [모듈]을 클릭한다
  3. 코드를 붙여 넣고 F5로 실행한다
📚
파워포인트 파일을 .pptm(매크로 사용 프레젠테이션)으로 저장해야 VBA 코드가 보존됩니다.

2. 템플릿 기반 문서 생성

템플릿 기반 자동화의 핵심은 플레이스홀더 치환입니다.

  • 템플릿 슬라이드에 {{이름}}, {{부서명}}, {{사번}} 등의 플레이스홀더를 미리 배치한다
  • VBA 코드가 엑셀에서 데이터를 읽어 플레이스홀더를 실제 값으로 바꾼다
  • 한 장의 템플릿으로 N장의 개별 문서를 생성할 수 있다

플레이스홀더 작성 규칙

  • 중괄호 두 개로 감싼다: {{항목명}}
  • 템플릿의 텍스트 상자 안에 직접 입력한다
  • 오타, 띄어쓰기에 주의한다 (코드에서 정확히 일치해야 치환됨)

3. 엑셀 데이터와 PPT 템플릿 연결

흐름도
[엑셀 명단]          [PPT 템플릿]           [결과물]
이름|사번|부서명  →  {{이름}}, {{사번}}  →  개인별 슬라이드
20명 데이터          1장 양식               20장 슬라이드
  • 엑셀 파일의 각 행이 하나의 슬라이드에 대응된다
  • PPT VBA에서 엑셀 파일을 열어 데이터를 읽는다
  • 1행은 헤더이므로 2행부터 데이터를 읽는다

4. 반복 슬라이드 생성

1장의 양식 슬라이드를 N장으로 복제하고, 각 슬라이드에 서로 다른 데이터를 주입합니다.

동작 순서

  1. 템플릿의 첫 번째 슬라이드를 복제한다
  2. 복제된 슬라이드의 모든 텍스트 상자를 순회한다
  3. 플레이스홀더를 해당 행의 데이터로 치환한다
  4. 다음 행에 대해 반복한다

5. PDF 저장 자동화

생성된 슬라이드를 개별 PDF로 저장합니다.

  • 각 슬라이드를 임시 프레젠테이션으로 복사한 뒤 PDF로 내보낸다
  • 파일명 규칙: 부서명_이름_수료증.pdf

6. 자동화에 적합한 PPT 업무

업무 유형예시
수료증/표창장교육 수료증, 우수사원 표창장
명세서/견적서급여명세서, 거래처 견적서
제안서/보고서 표지고객별 제안서 표지
회의자료부서별 실적 요약 슬라이드
💡
공통점: 양식이 고정되어 있고, 데이터만 다른 반복 작업

실습 파일

파일 위치설명
실습파일/실습_교육수료증-명단.xlsx수료자 20명 데이터 (시트명: 수료자명단)
실습파일/실습_교육수료증-템플릿.pptm수료증 양식 (플레이스홀더 + VBA 코드 포함)
💡
실습 파일 설명: .pptm 파일에는 VBA 코드가 이미 모듈로 삽입되어 있습니다. Alt+F8로 바로 실행할 수 있습니다.
📚
결과 저장 위치: VBA로 생성되는 수료증 PDF는 자동화실행결과/수료증생성결과_PDF/ 폴더에 저장됩니다.

수료증 템플릿 디자인

  • 슬라이드 크기: A4 세로 (210mm × 297mm)
  • 디자인: 네이비(#1B2A4A) + 골드(#C9A962) 이중 테두리, 코너 장식
  • 수여처: 한국GPT협회

수료증 명단 구조 (실습_교육수료증-명단.xlsx)

항목예시
A이름김민수
B사번EMP-2024-001
C부서명경영지원부
D교육명MS 오피스 VBA 자동화
E수료증번호CERT-2026-001
F이메일minsu.kim@example.com

부서 목록: 경영지원부, 인사부, 영업부, 연구개발부, 마케팅부 (각 4명씩 총 20명)

실습 1: 수료증 슬라이드 자동 생성

엑셀 명단을 읽어 20명분의 수료증 슬라이드를 자동으로 생성합니다. 1페이지(템플릿 양식)는 그대로 유지하고, 2페이지부터 개인별 수료증이 추가됩니다.

ChatGPT 프롬프트

ChatGPT 프롬프트
파워포인트 VBA 코드를 작성해줘.

- 현재 열려 있는 PPT의 첫 번째 슬라이드가 수료증 템플릿이야
- 같은 폴더에 있는 "실습_교육수료증-명단.xlsx" 파일을 읽어야 해
- 시트 이름은 "수료자명단"이야
- 엑셀 구조: A열=이름, B열=사번, C열=부서명, D열=교육명, E열=수료증번호, F열=이메일
- 1행은 헤더이고 2행부터 데이터야 (총 20명)
- 1페이지(템플릿 양식)는 그대로 유지해줘
- 2페이지부터 인원수만큼 슬라이드를 복제해서 개인 정보를 반영해줘
- 각 슬라이드에서 아래 플레이스홀더를 치환해:
  - {{이름}} → A열 값
  - {{사번}} → B열 값
  - {{부서명}} → C열 값
  - {{교육명}} → D열 값
  - {{수료증번호}} → E열 값
- 완료되면 MsgBox로 생성된 슬라이드 수를 알려줘

VBA 코드: 수료증 슬라이드 자동 생성 (Mod1_슬라이드생성)

VBA 코드
Sub 수료증_슬라이드_일괄생성()
    '=============================================================
    ' [모듈1] 엑셀 명단을 읽어 2페이지부터 수료증 슬라이드 생성
    '         1페이지 템플릿 양식은 그대로 유지
    '=============================================================

    Dim prs As Presentation
    Dim templateSlide As Slide
    Dim newSlide As Slide
    Dim xlApp As Object
    Dim xlWb As Object
    Dim xlWs As Object
    Dim lastRow As Long
    Dim i As Long
    Dim basePath As String
    Dim excelPath As String
    Dim shp As Shape
    Dim shpKey As String
    Dim origTexts As Object
    Dim totalCount As Long

    Set prs = ActivePresentation
    basePath = prs.Path & "\"
    excelPath = basePath & "실습_교육수료증-명단.xlsx"

    ' --- 엑셀 파일 존재 확인 ---
    If Dir(excelPath) = "" Then
        MsgBox "명단 파일을 찾을 수 없습니다." & vbCrLf & excelPath, vbExclamation, "오류"
        Exit Sub
    End If

    ' --- 기존 복사 슬라이드 제거 (템플릿 1장만 유지) ---
    Do While prs.Slides.Count > 1
        prs.Slides(prs.Slides.Count).Delete
    Loop

    ' --- 엑셀 열기 ---
    Set xlApp = CreateObject("Excel.Application")
    xlApp.Visible = False
    xlApp.DisplayAlerts = False
    Set xlWb = xlApp.Workbooks.Open(excelPath, ReadOnly:=True)
    Set xlWs = xlWb.Sheets("수료자명단")

    ' --- 데이터 행 수 파악 ---
    lastRow = xlWs.Cells(xlWs.Rows.Count, 1).End(-4162).Row

    If lastRow < 2 Then
        MsgBox "명단 데이터가 없습니다.", vbExclamation, "오류"
        GoTo CleanUp
    End If

    ' --- 템플릿 슬라이드 원본 텍스트 백업 ---
    Set origTexts = CreateObject("Scripting.Dictionary")
    Set templateSlide = prs.Slides(1)

    For Each shp In templateSlide.Shapes
        If shp.HasTextFrame Then
            shpKey = shp.Name
            origTexts.Add shpKey, shp.TextFrame.TextRange.Text
        End If
    Next shp

    totalCount = lastRow - 1

    ' === 인원수만큼 슬라이드 복사 (2페이지부터) ===
    For i = 2 To lastRow
        templateSlide.Copy
        DoEvents
        prs.Slides.Paste prs.Slides.Count + 1
        DoEvents
        Set newSlide = prs.Slides(prs.Slides.Count)

        ' 복사된 슬라이드의 텍스트를 원본으로 복원 후 치환
        For Each shp In newSlide.Shapes
            If shp.HasTextFrame Then
                shpKey = shp.Name
                If origTexts.Exists(shpKey) Then
                    shp.TextFrame.TextRange.Text = origTexts(shpKey)
                End If
            End If
        Next shp

        ' 데이터 삽입
        Call 슬라이드에_데이터삽입(newSlide, _
            Trim(CStr(xlWs.Cells(i, 1).Value)), _
            Trim(CStr(xlWs.Cells(i, 2).Value)), _
            Trim(CStr(xlWs.Cells(i, 3).Value)), _
            Trim(CStr(xlWs.Cells(i, 4).Value)), _
            Trim(CStr(xlWs.Cells(i, 5).Value)))
    Next i

CleanUp:
    xlWb.Close False
    xlApp.Quit
    Set xlWs = Nothing
    Set xlWb = Nothing
    Set xlApp = Nothing

    MsgBox "수료증 슬라이드 " & totalCount & "장 생성 완료!" & vbCrLf & _
           "(1페이지: 템플릿 / 2~" & (totalCount + 1) & "페이지: 개인별 수료증)", vbInformation, "완료"

End Sub


Private Sub 슬라이드에_데이터삽입(ByRef sld As Slide, _
    ByVal sName As String, ByVal sEmpNo As String, ByVal sDept As String, _
    ByVal sEdu As String, ByVal sCertNo As String)
    '----------------------------------------------
    ' 슬라이드 내 플레이스홀더를 실제 데이터로 치환
    '----------------------------------------------
    Dim shp As Shape
    Dim fullText As String
    For Each shp In sld.Shapes
        If shp.HasTextFrame Then
            fullText = shp.TextFrame.TextRange.Text

            If InStr(fullText, "{{") > 0 Then
                fullText = Replace(fullText, "{{이름}}", sName)
                fullText = Replace(fullText, "{{사번}}", sEmpNo)
                fullText = Replace(fullText, "{{부서명}}", sDept)
                fullText = Replace(fullText, "{{교육명}}", sEdu)
                fullText = Replace(fullText, "{{수료증번호}}", sCertNo)
                shp.TextFrame.TextRange.Text = fullText
            End If
        End If
    Next shp
End Sub
💡
참고: 한글 플레이스홀더를 직접 사용합니다. ChrW() 변환 없이 "{{이름}}" 형태로 작성하면 코드 가독성이 훨씬 좋습니다. 파워포인트 VBA는 유니코드를 기본 지원하므로 한글을 직접 사용해도 문제없습니다.

실행 결과

수료증 템플릿을 기반으로 1페이지(템플릿)는 유지되고, 2~21페이지에 20명분의 개별 수료증 슬라이드가 자동 생성됩니다.

수료증 전체 슬라이드 생성 결과

실습 2: 개인별 PDF 저장

개인별 슬라이드를 생성한 뒤, 각각을 새 PPTX에 복사하여 개별 PDF 파일로 저장합니다.

ChatGPT 프롬프트

ChatGPT 프롬프트
파워포인트 VBA 코드를 작성해줘.

- 같은 폴더에 있는 "실습_교육수료증-명단.xlsx"의 "수료자명단" 시트를 읽어야 해
- 엑셀 구조: A열=이름, B열=사번, C열=부서명, D열=교육명, E열=수료증번호
- 1행은 헤더이고 2행부터 데이터야 (총 20명)
- 코드 순서:
  1. 실습1처럼 인원수만큼 개인별 슬라이드를 2페이지부터 만든다
  2. 새 PPTX를 열고 개인 수료증 슬라이드 1장만 복사한다 (마스터 디자인, 레이아웃 유지)
  3. 그 PPTX를 PDF로 저장하고 닫는다
  4. 인원수만큼 반복한다
- 저장 폴더: "자동화실행결과\수료증생성결과_PDF" (같은 폴더 하위)
- 파일명 규칙: "수료증_이름_수료증번호.pdf"
- 완료 후 개인 슬라이드를 삭제하고 1페이지 템플릿만 남겨줘
- 완료되면 MsgBox로 생성된 PDF 수와 저장 위치를 알려줘

VBA 코드: 개인별 PDF 저장 (Mod2_PDF생성)

VBA 코드
Sub 수료증_개인별_PDF생성()
    '=============================================================
    ' [모듈2] 개인별 수료증 PDF 파일 생성
    '   1) 모듈1처럼 개인별 슬라이드를 모두 만든다
    '   2) 새 PPTX를 열고 개인 슬라이드 1장만 복사
    '   3) PDF로 저장하고 PPTX를 닫는다
    '   4) 인원수만큼 반복
    '   저장: 자동화실행결과\수료증생성결과_PDF\수료증_이름_번호.pdf
    '=============================================================

    Dim prs As Presentation
    Dim xlApp As Object
    Dim xlWb As Object
    Dim xlWs As Object
    Dim lastRow As Long
    Dim i As Long
    Dim basePath As String
    Dim excelPath As String
    Dim pdfFolder As String
    Dim pdfPath As String
    Dim fso As Object
    Dim shp As Shape
    Dim shpKey As String
    Dim origTexts As Object
    Dim totalCount As Long
    Dim successCount As Long
    Dim sName As String
    Dim sEmpNo As String
    Dim sDept As String
    Dim sEdu As String
    Dim sCertNo As String
    Dim fullText As String
    Dim parentFolder As String
    Dim tempPrs As Presentation
    Dim newSlide As Slide

    Set prs = ActivePresentation
    basePath = prs.Path & "\"
    excelPath = basePath & "실습_교육수료증-명단.xlsx"
    pdfFolder = basePath & "자동화실행결과\수료증생성결과_PDF"

    ' --- 엑셀 파일 존재 확인 ---
    If Dir(excelPath) = "" Then
        MsgBox "명단 파일을 찾을 수 없습니다." & vbCrLf & excelPath, vbExclamation, "오류"
        Exit Sub
    End If

    ' --- PDF 폴더 생성 (상위 폴더 포함) ---
    Set fso = CreateObject("Scripting.FileSystemObject")
    parentFolder = basePath & "자동화실행결과"
    If Not fso.FolderExists(parentFolder) Then
        fso.CreateFolder parentFolder
    End If
    If Not fso.FolderExists(pdfFolder) Then
        fso.CreateFolder pdfFolder
    End If

    ' --- 엑셀 열기 ---
    Set xlApp = CreateObject("Excel.Application")
    xlApp.Visible = False
    xlApp.DisplayAlerts = False
    Set xlWb = xlApp.Workbooks.Open(excelPath, ReadOnly:=True)
    Set xlWs = xlWb.Sheets("수료자명단")

    lastRow = xlWs.Cells(xlWs.Rows.Count, 1).End(-4162).Row

    If lastRow < 2 Then
        MsgBox "명단 데이터가 없습니다.", vbExclamation, "오류"
        GoTo CleanUp
    End If

    ' --- 기존 복사 슬라이드 제거 (템플릿 1장만 유지) ---
    Do While prs.Slides.Count > 1
        prs.Slides(prs.Slides.Count).Delete
    Loop

    ' --- 템플릿 슬라이드 원본 텍스트 백업 ---
    Set origTexts = CreateObject("Scripting.Dictionary")
    For Each shp In prs.Slides(1).Shapes
        If shp.HasTextFrame Then
            shpKey = shp.Name
            origTexts.Add shpKey, shp.TextFrame.TextRange.Text
        End If
    Next shp

    totalCount = lastRow - 1
    successCount = 0

    ' ========================================
    ' STEP 1: 인원수만큼 슬라이드 복사 & 데이터 삽입 (2페이지부터)
    ' ========================================
    For i = 2 To lastRow
        prs.Slides(1).Copy
        DoEvents
        prs.Slides.Paste prs.Slides.Count + 1
        DoEvents

        Set newSlide = prs.Slides(prs.Slides.Count)

        ' 텍스트 원본 복원
        For Each shp In newSlide.Shapes
            If shp.HasTextFrame Then
                shpKey = shp.Name
                If origTexts.Exists(shpKey) Then
                    shp.TextFrame.TextRange.Text = origTexts(shpKey)
                End If
            End If
        Next shp

        ' 데이터 삽입
        sName = Trim(CStr(xlWs.Cells(i, 1).Value))
        sEmpNo = Trim(CStr(xlWs.Cells(i, 2).Value))
        sDept = Trim(CStr(xlWs.Cells(i, 3).Value))
        sEdu = Trim(CStr(xlWs.Cells(i, 4).Value))
        sCertNo = Trim(CStr(xlWs.Cells(i, 5).Value))

        For Each shp In newSlide.Shapes
            If shp.HasTextFrame Then
                fullText = shp.TextFrame.TextRange.Text
                If InStr(fullText, "{{") > 0 Then
                    fullText = Replace(fullText, "{{이름}}", sName)
                    fullText = Replace(fullText, "{{사번}}", sEmpNo)
                    fullText = Replace(fullText, "{{부서명}}", sDept)
                    fullText = Replace(fullText, "{{교육명}}", sEdu)
                    fullText = Replace(fullText, "{{수료증번호}}", sCertNo)
                    shp.TextFrame.TextRange.Text = fullText
                End If
            End If
        Next shp
    Next i

    ' ========================================
    ' STEP 2: 개인별 슬라이드를 새 PPTX로 복사 → PDF 저장
    ' ========================================
    For i = 2 To prs.Slides.Count
        ' 슬라이드에서 이름, 수료증번호 추출 (파일명용)
        sName = Trim(CStr(xlWs.Cells(i, 1).Value))
        sCertNo = Trim(CStr(xlWs.Cells(i, 5).Value))

        ' 새 프레젠테이션 생성
        Set tempPrs = Application.Presentations.Add(msoFalse)

        ' 슬라이드 크기 맞추기 (A4 세로)
        tempPrs.PageSetup.SlideWidth = prs.PageSetup.SlideWidth
        tempPrs.PageSetup.SlideHeight = prs.PageSetup.SlideHeight

        ' 원본 슬라이드 복사 → 새 프레젠테이션에 붙여넣기
        prs.Slides(i).Copy
        DoEvents
        tempPrs.Slides.Paste
        DoEvents

        ' 마스터/레이아웃 디자인 유지
        tempPrs.Slides(1).Design = prs.Slides(i).Design
        tempPrs.Slides(1).CustomLayout = prs.Slides(i).CustomLayout

        ' PDF 저장
        pdfPath = pdfFolder & "\" & "수료증_" & sName & "_" & sCertNo & ".pdf"

        tempPrs.ExportAsFixedFormat _
            Path:=pdfPath, _
            FixedFormatType:=2, _
            Intent:=1, _
            FrameSlides:=0

        ' 임시 PPTX 닫기 (저장 안 함)
        tempPrs.Close
        Set tempPrs = Nothing

        successCount = successCount + 1
        DoEvents
    Next i

    ' --- 생성된 개인 슬라이드 제거 (템플릿만 남기기) ---
    Do While prs.Slides.Count > 1
        prs.Slides(prs.Slides.Count).Delete
    Loop

CleanUp:
    xlWb.Close False
    xlApp.Quit
    Set xlWs = Nothing
    Set xlWb = Nothing
    Set xlApp = Nothing
    Set fso = Nothing

    MsgBox "개인별 PDF " & successCount & "개 생성 완료!" & vbCrLf & _
           "저장 위치: " & pdfFolder, vbInformation, "완료"

End Sub

실행 결과

자동화실행결과/수료증생성결과_PDF/ 폴더 안에 20개의 개별 PDF 파일이 저장됩니다.

수료증 PDF 폴더 결과
생성된 파일 예시
자동화실행결과/수료증생성결과_PDF/
├── 수료증_김민수_CERT-2026-001.pdf
├── 수료증_이지은_CERT-2026-002.pdf
├── 수료증_박준혁_CERT-2026-003.pdf
├── 수료증_최수정_CERT-2026-004.pdf
├── 수료증_정우진_CERT-2026-005.pdf
├── 수료증_한서연_CERT-2026-006.pdf
└── ... (총 20개)

실습 요약

단계작업 내용결과
1실습_교육수료증-템플릿.pptm 열기플레이스홀더가 포함된 수료증 양식 + VBA 코드 확인
2Alt+F8수료증_슬라이드_일괄생성 실행1페이지(템플릿) + 20장의 개별 수료증 슬라이드 생성
3Alt+F8수료증_개인별_PDF생성 실행20개의 개별 PDF 파일 저장

주의사항

  1. 파일 형식: VBA 코드가 포함된 파일은 반드시 .pptm(매크로 사용 프레젠테이션)으로 저장해야 합니다. .pptx로 저장하면 VBA 코드가 삭제됩니다.
  2. 매크로 허용: .pptm 파일을 열 때 "매크로 사용" 또는 "콘텐츠 사용"을 클릭해야 VBA가 실행됩니다.
  3. 플레이스홀더 오타 주의: {{이름}}{{ 이름 }}은 다릅니다. 템플릿과 코드의 플레이스홀더가 정확히 일치해야 합니다.
  4. 파일 경로: PPT 파일과 엑셀 파일(실습_교육수료증-명단.xlsx)이 같은 폴더에 있어야 합니다.
  5. 엑셀이 이미 열려 있는 경우: 다른 프로세스에서 엑셀 파일을 열고 있으면 오류가 발생할 수 있습니다. 먼저 닫아주세요.
  6. PowerPoint VBA vs Excel VBA 차이점: Application.StatusBar는 엑셀 전용입니다. 파워포인트에서는 사용할 수 없으므로 주의하세요.

실습 과제

  1. 실습_교육수료증-템플릿.pptm을 열고 플레이스홀더({{이름}}, {{사번}} 등)를 확인하세요.
  2. Alt+F8수료증_슬라이드_일괄생성을 실행하여 2~21페이지에 20장의 슬라이드를 만드세요.
  3. Alt+F8수료증_개인별_PDF생성을 실행하여 자동화실행결과/수료증생성결과_PDF/ 폴더에 개별 PDF를 저장하세요.
  4. 생성된 PDF를 열어 내용이 올바르게 치환되었는지 확인하세요.
  5. (선택) Alt+F11로 VBA 편집기를 열어 Mod1_슬라이드생성, Mod2_PDF생성 모듈의 코드를 확인하세요.
💡
참고: 같은 수료증 PDF 생성을 파이썬으로도 할 수 있습니다. 7차시에서 다룹니다.

5차시. MS 워드 자동화

학습 목표

  • 워드 문서 자동화의 개념과 활용 사례를 이해한다
  • AI가 생성한 글을 워드 양식으로 옮기는 방법을 익힌다
  • Word VBA로 서식, 표, 문구 치환을 자동화한다
  • AI 생성 초안을 업무 문서 수준으로 완성하는 흐름을 실습한다

1. 워드 문서 자동화 개념

워드 자동화는 파워포인트 자동화와 같은 원리입니다:
"템플릿 + 데이터 + 치환/삽입 + 서식 일괄 적용"

워드 자동화가 효과적인 업무

  • 정기 보고서 (월간/분기 보고서)
  • 공문, 내부 문서
  • 계약서, 제안서
  • HR 분석 보고서

2. AI 생성 글을 워드 양식으로 옮기기

ChatGPT에게 보고서 초안을 요청하고, Word VBA로 서식을 자동 적용하는 흐름입니다.

흐름도
[1단계] ChatGPT로 보고서 초안 생성 (텍스트)
    ↓
[2단계] 워드 문서에 텍스트 삽입
    ↓
[3단계] VBA로 서식 자동 적용 (제목, 본문, 표, 여백 등)
    ↓
[4단계] PDF로 저장

3. Word VBA 기본

Word VBA도 Alt + F11로 편집기를 열고, 모듈을 삽입하여 사용합니다.

기본 동작

  • 문서 열기: Documents.Open
  • 텍스트 삽입: Selection.TypeText
  • 새 문단: Selection.TypeParagraph
  • 서식 제어: Selection.Style, Selection.Font, Selection.ParagraphFormat

4. 서식 자동화

VBA로 제어할 수 있는 주요 서식:

서식 항목VBA 속성예시
제목 스타일Selection.Style"제목 1", "제목 2"
폰트Selection.Font.Name"맑은 고딕"
글자 크기Selection.Font.Size11, 14, 18
줄간격ParagraphFormat.LineSpacingRule1.5줄
여백PageSetup.TopMargin2cm

5. 표와 그래프 편집 자동화

  • 표 삽입: ActiveDocument.Tables.Add
  • 표 너비 정렬: Table.AutoFitBehavior
  • 머리글 음영: Cell.Shading.BackgroundPatternColor
  • 테두리: Table.Borders

6. 문구 일괄 치환

Find.Execute를 사용하여 문서 전체에서 특정 문구를 일괄 치환할 수 있습니다.

활용 사례

  • 날짜 일괄 변경: 2025년 1월2025년 2월
  • 부서명 변경: 인사팀인사부
  • 회사명 변경

실습 흐름

  1. ChatGPT로 HR 분석 보고서 초안을 생성한다
  2. Word VBA로 보고서 서식을 자동 적용한다
  3. 표를 삽입하고 서식을 적용한다
  4. 문구를 일괄 치환한다
  5. PDF로 저장한다

실습 1: 워드 문서 생성 + 스타일 적용

ChatGPT로 생성한 HR 보고서 초안을 워드 문서로 만들고 스타일을 자동 적용합니다.

ChatGPT 프롬프트 (보고서 초안 생성)

ChatGPT 프롬프트 - 보고서 초안
HR 인사 분석 보고서 초안을 작성해줘.

- 보고서 제목: "2025년 상반기 인사 현황 분석 보고서"
- 구성:
  1. 개요 (3~4줄)
  2. 인력 현황 (부서별 인원, 직급별 분포)
  3. 퇴사 현황 분석 (퇴사율, 주요 원인)
  4. 교육 이수 현황
  5. 종합 의견 및 제언
- 각 섹션은 ##로 구분해줘
- 분량: A4 2~3페이지 분량

ChatGPT 프롬프트 (VBA 코드 생성)

ChatGPT 프롬프트 - VBA 코드
Word VBA 코드를 작성해줘.

- 새 워드 문서를 생성하고 HR 보고서 내용을 삽입해줘
- 페이지 설정: 여백 상하좌우 2cm
- 보고서 제목: "2025년 상반기 인사 현황 분석 보고서"
  - 스타일: 제목 1, 가운데 정렬, 맑은 고딕 18pt, 굵게
- 섹션 제목 5개: 개요, 인력 현황, 퇴사 현황 분석, 교육 이수 현황, 종합 의견 및 제언
  - 스타일: 제목 2, 맑은 고딕 14pt, 굵게
- 각 섹션 본문: 맑은 고딕 11pt, 줄간격 1.5줄
- 본문 내용은 각 섹션에 3~4줄씩 예시 텍스트를 넣어줘
- 한글 문자열은 ChrW()를 사용해줘
- 완료되면 MsgBox로 알려줘

VBA 코드: 워드 문서 생성 + 스타일 적용

VBA 코드
Sub CreateHRReport()
    Dim doc As Document
    Dim sel As Selection

    ' 새 문서 생성
    Set doc = Documents.Add

    ' 페이지 설정 - 여백 2cm (1cm = 28.35pt)
    With doc.PageSetup
        .TopMargin = CentimetersToPoints(2)
        .BottomMargin = CentimetersToPoints(2)
        .LeftMargin = CentimetersToPoints(2)
        .RightMargin = CentimetersToPoints(2)
    End With

    Set sel = Selection

    ' === 보고서 제목 ===
    ' "2025년 상반기 인사 현황 분석 보고서"
    sel.Style = ActiveDocument.Styles(ChrW(51228) & ChrW(47785) & " 1")  ' "제목 1"
    sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)  ' "맑은 고딕"
    sel.Font.Size = 18
    sel.Font.Bold = True
    sel.ParagraphFormat.Alignment = wdAlignParagraphCenter
    sel.TypeText "2025" & ChrW(45380) & " " & ChrW(49345) & ChrW(48152) & ChrW(44592) & " " & _
                 ChrW(51064) & ChrW(49324) & " " & ChrW(54788) & ChrW(54889) & " " & _
                 ChrW(48516) & ChrW(49437) & " " & ChrW(48372) & ChrW(44256) & ChrW(49436)
    ' "2025년 상반기 인사 현황 분석 보고서"
    sel.TypeParagraph
    sel.TypeParagraph

    ' === 1. 개요 ===
    sel.Style = ActiveDocument.Styles(ChrW(51228) & ChrW(47785) & " 2")  ' "제목 2"
    sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
    sel.Font.Size = 14
    sel.Font.Bold = True
    sel.ParagraphFormat.Alignment = wdAlignParagraphLeft
    sel.TypeText "1. " & ChrW(44060) & ChrW(50836)  ' "1. 개요"
    sel.TypeParagraph

    ' 개요 본문
    sel.Style = ActiveDocument.Styles(ChrW(48376) & ChrW(47928))  ' "본문"
    sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
    sel.Font.Size = 11
    sel.Font.Bold = False
    sel.ParagraphFormat.LineSpacingRule = wdLineSpace1pt5
    sel.ParagraphFormat.Alignment = wdAlignParagraphLeft
    sel.TypeText ChrW(48376) & " " & ChrW(48372) & ChrW(44256) & ChrW(49436) & ChrW(45716) & " 2025" & ChrW(45380) & " " & _
                 ChrW(49345) & ChrW(48152) & ChrW(44592) & " " & ChrW(51064) & ChrW(49324) & " " & _
                 ChrW(54788) & ChrW(54889) & ChrW(51012) & " " & ChrW(51333) & ChrW(54633) & ChrW(51201) & ChrW(51004) & ChrW(47196) & " " & _
                 ChrW(48516) & ChrW(49437) & ChrW(54616) & ChrW(50668) & " " & ChrW(51064) & ChrW(47141) & " " & _
                 ChrW(50868) & ChrW(50689) & ChrW(44284) & " " & ChrW(53748) & ChrW(49324) & " " & ChrW(46041) & ChrW(54693) & ", " & _
                 ChrW(44368) & ChrW(50977) & " " & ChrW(51060) & ChrW(49688) & " " & ChrW(54788) & ChrW(54889) & ChrW(51012) & " " & _
                 ChrW(45812) & ChrW(44256) & " " & ChrW(51080) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
    ' "본 보고서는 2025년 상반기 인사 현황을 종합적으로 분석하여 인력 운영과 퇴사 동향, 교육 이수 현황을 담고 있습니다."
    sel.TypeParagraph
    sel.TypeParagraph

    ' === 2. 인력 현황 ===
    sel.Style = ActiveDocument.Styles(ChrW(51228) & ChrW(47785) & " 2")
    sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
    sel.Font.Size = 14
    sel.Font.Bold = True
    sel.TypeText "2. " & ChrW(51064) & ChrW(47141) & " " & ChrW(54788) & ChrW(54889)  ' "2. 인력 현황"
    sel.TypeParagraph

    sel.Style = ActiveDocument.Styles(ChrW(48376) & ChrW(47928))
    sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
    sel.Font.Size = 11
    sel.Font.Bold = False
    sel.ParagraphFormat.LineSpacingRule = wdLineSpace1pt5
    sel.TypeText ChrW(51204) & ChrW(52404) & " " & ChrW(51064) & ChrW(50896) & " 1,470" & ChrW(47749) & ChrW(51004) & " " & _
                 ChrW(45824) & ChrW(54644) & ", " & ChrW(48512) & ChrW(49436) & ChrW(48324) & " " & _
                 ChrW(51064) & ChrW(50896) & " " & ChrW(48516) & ChrW(54252) & ChrW(45716) & " " & _
                 ChrW(50500) & ChrW(47000) & ChrW(50752) & " " & ChrW(44057) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
    ' "전체 인원 1,470명에 대해, 부서별 인원 분포는 아래와 같습니다."
    sel.TypeParagraph
    sel.TypeParagraph

    ' === 3. 퇴사 현황 분석 ===
    sel.Style = ActiveDocument.Styles(ChrW(51228) & ChrW(47785) & " 2")
    sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
    sel.Font.Size = 14
    sel.Font.Bold = True
    sel.TypeText "3. " & ChrW(53748) & ChrW(49324) & " " & ChrW(54788) & ChrW(54889) & " " & ChrW(48516) & ChrW(49437)
    ' "3. 퇴사 현황 분석"
    sel.TypeParagraph

    sel.Style = ActiveDocument.Styles(ChrW(48376) & ChrW(47928))
    sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
    sel.Font.Size = 11
    sel.Font.Bold = False
    sel.ParagraphFormat.LineSpacingRule = wdLineSpace1pt5
    sel.TypeText ChrW(49345) & ChrW(48152) & ChrW(44592) & " " & ChrW(53748) & ChrW(49324) & ChrW(50984) & ChrW(51008) & " 16.1%" & _
                 ChrW(47196) & " " & ChrW(51204) & ChrW(45380) & " " & ChrW(46041) & ChrW(44592) & " " & _
                 ChrW(45824) & ChrW(48708) & " " & ChrW(49548) & ChrW(54253) & " " & ChrW(44048) & ChrW(49548) & ChrW(54616) & ChrW(50688) & ChrW(49845) & ChrW(45768) & ChrW(45796) & "."
    ' "상반기 퇴사율은 16.1%로 전년 동기 대비 소폭 감소하였습니다."
    sel.TypeParagraph
    sel.TypeParagraph

    ' === 4. 교육 이수 현황 ===
    sel.Style = ActiveDocument.Styles(ChrW(51228) & ChrW(47785) & " 2")
    sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
    sel.Font.Size = 14
    sel.Font.Bold = True
    sel.TypeText "4. " & ChrW(44368) & ChrW(50977) & " " & ChrW(51060) & ChrW(49688) & " " & ChrW(54788) & ChrW(54889)
    ' "4. 교육 이수 현황"
    sel.TypeParagraph

    sel.Style = ActiveDocument.Styles(ChrW(48376) & ChrW(47928))
    sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
    sel.Font.Size = 11
    sel.Font.Bold = False
    sel.ParagraphFormat.LineSpacingRule = wdLineSpace1pt5
    sel.TypeText ChrW(49345) & ChrW(48152) & ChrW(44592) & " " & ChrW(54596) & ChrW(49688) & " " & ChrW(44368) & ChrW(50977) & " " & _
                 ChrW(51060) & ChrW(49688) & ChrW(50984) & ChrW(51008) & " 78.5%" & ChrW(51060) & ChrW(47728) & ", " & _
                 ChrW(47785) & ChrW(54364) & " " & ChrW(44368) & ChrW(50977) & " " & ChrW(51060) & ChrW(49688) & ChrW(50984) & " " & _
                 ChrW(51228) & ChrW(44256) & ChrW(47484) & " " & ChrW(54596) & ChrW(50836) & ChrW(54633) & ChrW(45768) & ChrW(45796) & "."
    ' "상반기 필수 교육 이수율은 78.5%이며, 목표 교육 이수율 제고를 위해 필요합니다."
    sel.TypeParagraph
    sel.TypeParagraph

    ' === 5. 종합 의견 및 제언 ===
    sel.Style = ActiveDocument.Styles(ChrW(51228) & ChrW(47785) & " 2")
    sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
    sel.Font.Size = 14
    sel.Font.Bold = True
    sel.TypeText "5. " & ChrW(51333) & ChrW(54633) & " " & ChrW(51032) & ChrW(44204) & " " & _
                 ChrW(48143) & " " & ChrW(51228) & ChrW(50616)
    ' "5. 종합 의견 및 제언"
    sel.TypeParagraph

    sel.Style = ActiveDocument.Styles(ChrW(48376) & ChrW(47928))
    sel.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)
    sel.Font.Size = 11
    sel.Font.Bold = False
    sel.ParagraphFormat.LineSpacingRule = wdLineSpace1pt5
    sel.TypeText ChrW(51064) & ChrW(47141) & " " & ChrW(50868) & ChrW(50689) & ChrW(51032) & " " & _
                 ChrW(54952) & ChrW(50984) & ChrW(49457) & ChrW(51012) & " " & ChrW(45458) & ChrW(51060) & ChrW(44256) & " " & _
                 ChrW(53748) & ChrW(49324) & ChrW(50984) & " " & ChrW(44048) & ChrW(49548) & ChrW(47484) & " " & _
                 ChrW(50948) & ChrW(54644) & " " & ChrW(51648) & ChrW(49549) & ChrW(51201) & ChrW(51064) & " " & _
                 ChrW(51064) & ChrW(49324) & " " & ChrW(44288) & ChrW(47532) & ChrW(44032) & " " & _
                 ChrW(54596) & ChrW(50836) & ChrW(54633) & ChrW(45768) & ChrW(45796) & "."
    ' "인력 운영의 효율성을 높이고 퇴사율 감소를 위해 지속적인 인사 관리가 필요합니다."
    sel.TypeParagraph

    ' 완료 메시지
    MsgBox ChrW(48372) & ChrW(44256) & ChrW(49436) & " " & ChrW(47928) & ChrW(49436) & " " & _
           ChrW(49373) & ChrW(49457) & " " & ChrW(48143) & " " & ChrW(49436) & ChrW(49885) & " " & _
           ChrW(51201) & ChrW(50857) & " " & ChrW(50756) & ChrW(47308) & "!", vbInformation
    ' "보고서 문서 생성 및 서식 적용 완료!"
End Sub

실습 2: 표 삽입 + 서식 적용

보고서에 부서별 인원 현황 표를 삽입하고 서식을 자동 적용합니다.

ChatGPT 프롬프트

ChatGPT 프롬프트
Word VBA 코드를 작성해줘.

- 현재 열려 있는 워드 문서의 커서 위치에 표를 삽입해줘
- 표 구조: 6행 3열
  - 1행(머리글): 부서명, 인원수, 비율
  - 2행: 경영지원부, 63명, 4.3%
  - 3행: 인사부, 52명, 3.5%
  - 4행: 영업부, 446명, 30.3%
  - 5행: 연구개발부, 961명, 65.4%
  - 6행(합계): 합계, 1,470명, 100%
- 서식:
  - 표 너비: 문서 폭에 자동 맞춤
  - 머리글 행: 배경색 진한 파랑(RGB 0,51,102), 글자 흰색, 굵게, 가운데 정렬
  - 합계 행: 굵게, 배경색 연한 회색
  - 모든 셀: 맑은 고딕 10pt, 세로 가운데 정렬
  - 테두리: 모든 테두리 표시
- 한글 문자열은 ChrW()를 사용해줘

VBA 코드: 표 삽입 + 서식

VBA 코드
Sub InsertDepartmentTable()
    Dim doc As Document
    Dim tbl As Table
    Dim rng As Range

    Set doc = ActiveDocument
    Set rng = Selection.Range

    ' 6행 3열 표 삽입
    Set tbl = doc.Tables.Add(rng, 6, 3)

    ' 표 너비 자동 맞춤
    tbl.AutoFitBehavior wdAutoFitWindow

    ' 머리글 행 데이터 입력
    tbl.Cell(1, 1).Range.Text = ChrW(48512) & ChrW(49436) & ChrW(47749)  ' "부서명"
    tbl.Cell(1, 2).Range.Text = ChrW(51064) & ChrW(50896) & ChrW(49688)  ' "인원수"
    tbl.Cell(1, 3).Range.Text = ChrW(48708) & ChrW(50984)  ' "비율"

    ' 데이터 입력
    tbl.Cell(2, 1).Range.Text = ChrW(44221) & ChrW(50689) & ChrW(51648) & ChrW(50896) & ChrW(48512)  ' "경영지원부"
    tbl.Cell(2, 2).Range.Text = "63" & ChrW(47749)  ' "63명"
    tbl.Cell(2, 3).Range.Text = "4.3%"

    tbl.Cell(3, 1).Range.Text = ChrW(51064) & ChrW(49324) & ChrW(48512)  ' "인사부"
    tbl.Cell(3, 2).Range.Text = "52" & ChrW(47749)
    tbl.Cell(3, 3).Range.Text = "3.5%"

    tbl.Cell(4, 1).Range.Text = ChrW(50689) & ChrW(50629) & ChrW(48512)  ' "영업부"
    tbl.Cell(4, 2).Range.Text = "446" & ChrW(47749)
    tbl.Cell(4, 3).Range.Text = "30.3%"

    tbl.Cell(5, 1).Range.Text = ChrW(50672) & ChrW(44396) & ChrW(44060) & ChrW(48156) & ChrW(48512)  ' "연구개발부"
    tbl.Cell(5, 2).Range.Text = "961" & ChrW(47749)
    tbl.Cell(5, 3).Range.Text = "65.4%"

    tbl.Cell(6, 1).Range.Text = ChrW(54633) & ChrW(44228)  ' "합계"
    tbl.Cell(6, 2).Range.Text = "1,470" & ChrW(47749)
    tbl.Cell(6, 3).Range.Text = "100%"

    ' === 서식 적용 ===

    ' 전체 표 폰트 설정
    tbl.Range.Font.Name = ChrW(47567) & ChrW(51008) & " " & ChrW(44256) & ChrW(46357)  ' "맑은 고딕"
    tbl.Range.Font.Size = 10

    ' 전체 셀 세로 가운데 정렬
    Dim c As Cell
    For Each c In tbl.Range.Cells
        c.VerticalAlignment = wdCellAlignVerticalCenter
        c.Range.ParagraphFormat.Alignment = wdAlignParagraphCenter
    Next c

    ' 머리글 행 서식 (1행)
    Dim i As Long
    For i = 1 To 3
        With tbl.Cell(1, i)
            .Shading.BackgroundPatternColor = RGB(0, 51, 102)
            .Range.Font.Color = RGB(255, 255, 255)
            .Range.Font.Bold = True
        End With
    Next i

    ' 합계 행 서식 (6행)
    For i = 1 To 3
        With tbl.Cell(6, i)
            .Shading.BackgroundPatternColor = RGB(217, 217, 217)
            .Range.Font.Bold = True
        End With
    Next i

    ' 테두리 설정
    With tbl.Borders
        .InsideLineStyle = wdLineStyleSingle
        .OutsideLineStyle = wdLineStyleSingle
        .InsideLineWidth = wdLineWidth050pt
        .OutsideLineWidth = wdLineWidth050pt
    End With

    MsgBox ChrW(54364) & " " & ChrW(49341) & ChrW(51077) & " " & ChrW(48143) & " " & _
           ChrW(49436) & ChrW(49885) & " " & ChrW(51201) & ChrW(50857) & " " & _
           ChrW(50756) & ChrW(47308) & "!", vbInformation
    ' "표 삽입 및 서식 적용 완료!"
End Sub

실습 3: 문구 일괄 치환

보고서 내 특정 문구를 일괄 치환합니다.

ChatGPT 프롬프트

ChatGPT 프롬프트
Word VBA 코드를 작성해줘.

- 현재 열려 있는 워드 문서에서 문구를 일괄 치환해줘
- 치환 목록:
  1. "2025년 상반기" → "2025년 하반기"
  2. "인사팀" → "인사부"
  3. "작성일: 2025.06.30" → "작성일: 2025.12.31"
- 각 치환 후 몇 건이 변경되었는지 카운트해줘
- 한글 문자열은 ChrW()를 사용해줘
- 완료되면 MsgBox로 치환 결과를 알려줘

VBA 코드: 문구 일괄 치환

VBA 코드
Sub ReplaceTextInDocument()
    Dim doc As Document
    Dim totalCount As Long
    Dim msg As String

    Set doc = ActiveDocument
    totalCount = 0
    msg = ""

    ' --- 치환 1: "2025년 상반기" → "2025년 하반기" ---
    Dim findText1 As String
    Dim replaceText1 As String
    Dim count1 As Long

    findText1 = "2025" & ChrW(45380) & " " & ChrW(49345) & ChrW(48152) & ChrW(44592)
    ' "2025년 상반기"
    replaceText1 = "2025" & ChrW(45380) & " " & ChrW(54616) & ChrW(48152) & ChrW(44592)
    ' "2025년 하반기"

    count1 = CountAndReplace(doc, findText1, replaceText1)
    msg = msg & findText1 & " -> " & replaceText1 & ": " & count1 & ChrW(44148) & vbCrLf
    totalCount = totalCount + count1

    ' --- 치환 2: "인사팀" → "인사부" ---
    Dim findText2 As String
    Dim replaceText2 As String
    Dim count2 As Long

    findText2 = ChrW(51064) & ChrW(49324) & ChrW(54016)   ' "인사팀"
    replaceText2 = ChrW(51064) & ChrW(49324) & ChrW(48512) ' "인사부"

    count2 = CountAndReplace(doc, findText2, replaceText2)
    msg = msg & findText2 & " -> " & replaceText2 & ": " & count2 & ChrW(44148) & vbCrLf
    totalCount = totalCount + count2

    ' --- 치환 3: "작성일: 2025.06.30" → "작성일: 2025.12.31" ---
    Dim findText3 As String
    Dim replaceText3 As String
    Dim count3 As Long

    findText3 = ChrW(51089) & ChrW(49457) & ChrW(51068) & ": 2025.06.30"   ' "작성일: 2025.06.30"
    replaceText3 = ChrW(51089) & ChrW(49457) & ChrW(51068) & ": 2025.12.31" ' "작성일: 2025.12.31"

    count3 = CountAndReplace(doc, findText3, replaceText3)
    msg = msg & findText3 & " -> " & replaceText3 & ": " & count3 & ChrW(44148) & vbCrLf
    totalCount = totalCount + count3

    ' 결과 메시지
    msg = ChrW(47928) & ChrW(44396) & " " & ChrW(51068) & ChrW(44292) & " " & _
          ChrW(52824) & ChrW(54872) & " " & ChrW(50756) & ChrW(47308) & "!" & vbCrLf & vbCrLf & msg & _
          vbCrLf & ChrW(52509) & " " & ChrW(52824) & ChrW(54872) & " " & ChrW(44148) & ChrW(49688) & ": " & totalCount & ChrW(44148)
    ' "문구 일괄 치환 완료!" & 상세내역 & "총 치환 건수: N건"

    MsgBox msg, vbInformation
End Sub

Function CountAndReplace(doc As Document, findStr As String, replStr As String) As Long
    Dim cnt As Long
    cnt = 0

    ' 먼저 횟수 세기
    With doc.Content.Find
        .ClearFormatting
        .Text = findStr
        .Forward = True
        .Wrap = wdFindStop
        .MatchCase = False
        .MatchWholeWord = False

        Do While .Execute
            cnt = cnt + 1
        Loop
    End With

    ' 실제 치환
    With doc.Content.Find
        .ClearFormatting
        .Replacement.ClearFormatting
        .Text = findStr
        .Replacement.Text = replStr
        .Forward = True
        .Wrap = wdFindContinue
        .MatchCase = False
        .MatchWholeWord = False
        .Execute Replace:=wdReplaceAll
    End With

    CountAndReplace = cnt
End Function

실습 요약

단계작업 내용결과
1ChatGPT로 보고서 초안 생성5개 섹션의 HR 보고서 텍스트
2Word VBA로 문서 생성 + 스타일 적용제목/본문 서식이 적용된 워드 문서
3표 삽입 + 서식 적용부서별 인원 현황 표 (머리글 음영, 테두리)
4문구 일괄 치환날짜, 부서명 등 일괄 변경
5PDF 저장최종 보고서를 PDF로 내보내기

주의사항

  1. 스타일 이름 확인: 워드의 스타일 이름은 언어 설정에 따라 다를 수 있습니다. 한글 워드에서는 "제목 1", "본문" 등을 사용합니다.
  2. 커서 위치: 표 삽입 시 커서가 원하는 위치에 있는지 확인하세요.
  3. 문구 치환 주의: 치환할 문구가 정확히 일치해야 합니다. 띄어쓰기나 특수문자 차이에 유의하세요.
  4. 저장 형식: VBA 코드를 포함하려면 .docm(매크로 사용 문서)으로 저장하세요.

실습 과제

  1. ChatGPT에게 HR 분석 보고서 초안을 요청하세요.
  2. Word VBA로 보고서를 생성하고 서식을 적용하세요.
  3. 부서별 인원 현황 표를 삽입하고 서식을 적용하세요.
  4. 문구 일괄 치환 기능을 테스트하세요.
  5. 완성된 보고서를 PDF로 저장하세요. (파일 → 다른 이름으로 저장 → PDF 선택)

6차시. VBA를 이용한 이메일 발송 자동화

학습 목표

  • 네이버 메일 SMTP를 이용한 이메일 자동 발송 원리를 이해한다
  • 엑셀 명단과 첨부 파일을 매칭하여 자동 발송하는 VBA를 작성한다
  • 제목과 본문을 개인화하여 발송하는 방법을 익힌다
  • 보안 원칙(비밀번호 관리)을 이해하고 실천한다

1. 이메일 자동 발송 개념

엑셀 VBA에서 CDO(Collaboration Data Objects)를 사용하면 SMTP 서버를 통해 이메일을 자동으로 발송할 수 있습니다.

흐름도
[엑셀 명단]              [첨부 파일 폴더]           [발송]
부서명 | 이메일      →    부서명.xlsx              →  SMTP 서버
경영지원부 | a@b.com      경영지원부_인사현황.xlsx      → 수신자에게 전달

자동화 흐름

  1. 엑셀에서 수신자 명단(부서명, 이메일)을 읽는다
  2. 부서명에 해당하는 첨부 파일을 찾는다
  3. 제목과 본문을 개인화한다
  4. SMTP 서버를 통해 이메일을 발송한다
  5. 발송 결과를 엑셀에 기록한다

2. 네이버 POP3/SMTP 활성화

네이버 메일에서 SMTP 발송을 사용하려면 먼저 설정을 활성화해야 합니다.

설정 방법

  1. 네이버 메일 접속 → 환경설정 (톱니바퀴 아이콘)
  2. [POP3/IMAP 설정] 탭 클릭
  3. POP3/SMTP 사용 → "사용함"으로 변경
  4. 저장

네이버 SMTP 서버 정보

항목
SMTP 서버smtp.naver.com
포트465 (SSL)
인증 방식SSL/TLS
아이디네이버 아이디
비밀번호애플리케이션 비밀번호

3. 애플리케이션 비밀번호 생성

2단계 인증을 사용하는 경우, 일반 비밀번호 대신 애플리케이션 비밀번호를 생성해야 합니다.

생성 방법

  1. 네이버 → 내 정보 → 보안 설정
  2. 2단계 인증 → 관리
  3. 애플리케이션 비밀번호 생성
  4. 생성된 비밀번호를 메모 (한 번만 표시됨)
⚠️
2단계 인증을 사용하지 않는 경우에는 네이버 로그인 비밀번호를 그대로 사용할 수 있지만, 보안을 위해 애플리케이션 비밀번호 사용을 권장합니다.

4. 이메일 VBA 프롬프트 작성법

ChatGPT에게 이메일 발송 VBA를 요청할 때 아래 정보를 포함합니다:

  • SMTP 서버 주소와 포트
  • 인증 방식 (SSL)
  • 발신자 이메일
  • 수신자 명단 구조 (어떤 열에 무엇이 있는지)
  • 첨부 파일 위치와 파일명 규칙
  • 제목/본문 템플릿
  • 비밀번호 입력 방식 (InputBox)

5. 수신자 명단 구조

항목예시
A부서명경영지원부
B이메일manager@company.com
C발송상태(VBA가 자동 기록)

6. 제목/본문 개인화

제목 예시: [인사부] 2025년 상반기 인사현황 자료 송부

본문 예시

이메일 본문 템플릿
경영지원부 담당자님께,

안녕하세요. 인사부입니다.
2025년 상반기 인사현황 자료를 송부드립니다.
첨부 파일을 확인해 주시기 바랍니다.

감사합니다.
인사부 드림

7. 보안 원칙

🚨
반드시 지켜야 할 보안 규칙
규칙설명
코드에 비밀번호 직접 입력 금지InputBox로 실행 시 입력받도록 한다
프롬프트에 비밀번호 입력 금지ChatGPT 대화에 실제 비밀번호를 넣지 않는다
실습 후 비밀번호 삭제애플리케이션 비밀번호를 삭제하거나 변경한다
발송 테스트는 본인 주소로모든 수신 이메일을 본인 주소로 바꿔서 테스트한다

실습 흐름

  1. 네이버 SMTP 설정을 활성화한다
  2. 애플리케이션 비밀번호를 생성한다
  3. 엑셀에 수신자 명단을 작성한다
  4. VBA 이메일 발송 코드를 작성한다
  5. 본인 주소로 발송 테스트한다
  6. 발송 완료 후 인증 정보를 제거한다

실습 1: 이메일 발송 VBA

부서별 파일을 각 담당자에게 자동 첨부 발송합니다.

ChatGPT 프롬프트

ChatGPT 프롬프트
엑셀 VBA 코드를 작성해줘. CDO.Message를 이용한 이메일 자동 발송이야.

- SMTP 서버: smtp.naver.com, 포트 465, SSL 사용
- 발신자 이메일: InputBox로 입력받아
- 비밀번호: InputBox로 입력받아 (코드에 직접 넣지 않아)
- 수신자 명단은 현재 시트에 있어:
  - A열: 부서명 (2행부터 데이터)
  - B열: 이메일
  - C열: 발송상태 (빈칸이면 발송, "완료"면 건너뜀)
- 첨부 파일: 엑셀 파일과 같은 폴더의 "부서별파일" 하위 폴더에서 "부서명_인사현황.xlsx" 파일을 찾아
- 이메일 제목: "[부서명] 2025년 상반기 인사현황 자료 송부"
- 이메일 본문: HTML 형식으로 아래 내용 포함
  - "부서명 담당자님께," (줄바꿈)
  - "안녕하세요. 인사부입니다." (줄바꿈)
  - "2025년 상반기 인사현황 자료를 송부드립니다." (줄바꿈)
  - "첨부 파일을 확인해 주시기 바랍니다." (줄바꿈 2번)
  - "감사합니다." (줄바꿈)
  - "인사부 드림"
- 발송 성공하면 C열에 "완료"와 발송 시각 기록
- 발송 실패하면 C열에 "실패: 에러메시지" 기록
- 한글 문자열은 ChrW()를 사용해줘
- 각 발송 사이에 2초 대기 (서버 부하 방지)
- 완료되면 MsgBox로 발송 건수를 알려줘

VBA 코드: 이메일 발송 (CDO.Message)

VBA 코드
Sub SendEmailWithAttachment()
    Dim ws As Worksheet
    Dim lastRow As Long
    Dim i As Long
    Dim sendCount As Long
    Dim failCount As Long

    Dim senderEmail As String
    Dim senderPassword As String
    Dim basePath As String
    Dim attachFolderPath As String

    Set ws = ActiveSheet
    lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row

    ' 발신자 정보 입력받기 (코드에 직접 넣지 않음)
    senderEmail = InputBox(ChrW(48156) & ChrW(49888) & ChrW(51088) & " " & ChrW(51060) & ChrW(47700) & ChrW(51068) & ChrW(51012) & " " & ChrW(51077) & ChrW(47141) & ChrW(54616) & ChrW(49464) & ChrW(50836) & ":" & vbCrLf & _
                           ChrW(50696) & ") userid@naver.com", _
                           ChrW(48156) & ChrW(49888) & ChrW(51088) & " " & ChrW(51060) & ChrW(47700) & ChrW(51068))
    ' "발신자 이메일을 입력하세요: 예) userid@naver.com"
    If senderEmail = "" Then Exit Sub

    senderPassword = InputBox(ChrW(50528) & ChrW(54540) & ChrW(47532) & ChrW(52992) & ChrW(51060) & ChrW(49496) & " " & _
                              ChrW(48708) & ChrW(48128) & ChrW(48264) & ChrW(54840) & ChrW(47484) & " " & ChrW(51077) & ChrW(47141) & ChrW(54616) & ChrW(49464) & ChrW(50836) & ":", _
                              ChrW(48708) & ChrW(48128) & ChrW(48264) & ChrW(54840))
    ' "애플리케이션 비밀번호를 입력하세요:", "비밀번호"
    If senderPassword = "" Then Exit Sub

    ' 첨부 파일 폴더 경로
    basePath = ThisWorkbook.Path
    ' "부서별파일" 폴더
    attachFolderPath = basePath & "\" & ChrW(48512) & ChrW(49436) & ChrW(48324) & ChrW(54028) & ChrW(51068) & "\"

    sendCount = 0
    failCount = 0

    For i = 2 To lastRow
        ' C열이 비어있는 경우에만 발송
        If Trim(ws.Cells(i, 3).Value) = "" Then

            Dim deptName As String
            Dim recipientEmail As String
            Dim attachFilePath As String
            Dim emailSubject As String
            Dim emailBody As String

            deptName = ws.Cells(i, 1).Value      ' 부서명
            recipientEmail = ws.Cells(i, 2).Value ' 이메일

            ' 첨부 파일 경로: 부서명_인사현황.xlsx
            ' 인사현황 = ChrW(51064) & ChrW(49324) & ChrW(54788) & ChrW(54889)
            attachFilePath = attachFolderPath & deptName & "_" & _
                             ChrW(51064) & ChrW(49324) & ChrW(54788) & ChrW(54889) & ".xlsx"

            ' 이메일 제목: "[부서명] 2025년 상반기 인사현황 자료 송부"
            emailSubject = "[" & deptName & "] 2025" & ChrW(45380) & " " & _
                           ChrW(49345) & ChrW(48152) & ChrW(44592) & " " & _
                           ChrW(51064) & ChrW(49324) & ChrW(54788) & ChrW(54889) & " " & _
                           ChrW(51088) & ChrW(47308) & " " & ChrW(49569) & ChrW(48512)
            ' "[부서명] 2025년 상반기 인사현황 자료 송부"

            ' 이메일 본문 (HTML)
            emailBody = "<html><body style='font-family: Malgun Gothic, sans-serif; font-size: 10pt;'>"
            emailBody = emailBody & "<p>" & deptName & " " & _
                        ChrW(45812) & ChrW(45817) & ChrW(51088) & ChrW(45784) & ChrW(44760) & ",</p>"
            emailBody = emailBody & "<p>" & ChrW(50504) & ChrW(45397) & ChrW(54616) & ChrW(49464) & ChrW(50836) & ". " & _
                        ChrW(51064) & ChrW(49324) & ChrW(48512) & ChrW(51077) & ChrW(45768) & ChrW(45796) & ".</p>"
            emailBody = emailBody & "<p>2025" & ChrW(45380) & " " & _
                        ChrW(49345) & ChrW(48152) & ChrW(44592) & " " & _
                        ChrW(51064) & ChrW(49324) & ChrW(54788) & ChrW(54889) & " " & _
                        ChrW(51088) & ChrW(47308) & ChrW(47484) & " " & _
                        ChrW(49569) & ChrW(48512) & ChrW(46300) & ChrW(47549) & ChrW(45768) & ChrW(45796) & ".</p>"
            emailBody = emailBody & "<p>" & ChrW(52392) & ChrW(48512) & " " & _
                        ChrW(54028) & ChrW(51068) & ChrW(51012) & " " & _
                        ChrW(54869) & ChrW(51064) & ChrW(54644) & " " & _
                        ChrW(51452) & ChrW(49884) & ChrW(44592) & " " & _
                        ChrW(48148) & ChrW(46989) & ChrW(45768) & ChrW(45796) & ".</p>"
            emailBody = emailBody & "<br>"
            emailBody = emailBody & "<p>" & ChrW(44048) & ChrW(49324) & ChrW(54633) & ChrW(45768) & ChrW(45796) & ".</p>"
            emailBody = emailBody & "<p>" & ChrW(51064) & ChrW(49324) & ChrW(48512) & " " & _
                        ChrW(46300) & ChrW(47548) & "</p>"
            emailBody = emailBody & "</body></html>"

            ' CDO.Message로 이메일 발송
            On Error Resume Next

            Dim cdoMsg As Object
            Dim cdoConfig As Object

            Set cdoMsg = CreateObject("CDO.Message")
            Set cdoConfig = CreateObject("CDO.Configuration")

            With cdoConfig.Fields
                .Item("http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
                .Item("http://schemas.microsoft.com/cdo/configuration/smtpserver") = "smtp.naver.com"
                .Item("http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 465
                .Item("http://schemas.microsoft.com/cdo/configuration/smtpusessl") = True
                .Item("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate") = 1
                .Item("http://schemas.microsoft.com/cdo/configuration/sendusername") = senderEmail
                .Item("http://schemas.microsoft.com/cdo/configuration/sendpassword") = senderPassword
                .Item("http://schemas.microsoft.com/cdo/configuration/smtpconnectiontimeout") = 30
                .Update
            End With

            With cdoMsg
                Set .Configuration = cdoConfig
                .From = senderEmail
                .To = recipientEmail
                .Subject = emailSubject
                .HTMLBody = emailBody

                ' 첨부 파일 추가 (파일이 존재하는 경우)
                If Dir(attachFilePath) <> "" Then
                    .AddAttachment attachFilePath
                End If

                .Send
            End With

            If Err.Number = 0 Then
                ' 발송 성공: C열에 "완료" + 시각 기록
                ws.Cells(i, 3).Value = ChrW(50756) & ChrW(47308) & " (" & Format(Now, "yyyy-mm-dd hh:nn:ss") & ")"
                ' "완료 (2025-01-15 14:30:00)"
                sendCount = sendCount + 1
            Else
                ' 발송 실패: C열에 에러 메시지 기록
                ws.Cells(i, 3).Value = ChrW(49892) & ChrW(54056) & ": " & Err.Description
                ' "실패: 에러메시지"
                failCount = failCount + 1
                Err.Clear
            End If

            On Error GoTo 0

            Set cdoMsg = Nothing
            Set cdoConfig = Nothing

            ' 서버 부하 방지를 위한 2초 대기
            Application.Wait Now + TimeValue("00:00:02")
        End If
    Next i

    ' 비밀번호 변수 초기화 (메모리에서 제거)
    senderPassword = ""

    ' 완료 메시지
    MsgBox ChrW(51060) & ChrW(47700) & ChrW(51068) & " " & ChrW(48156) & ChrW(49569) & " " & _
           ChrW(50756) & ChrW(47308) & "!" & vbCrLf & vbCrLf & _
           ChrW(49457) & ChrW(44277) & ": " & sendCount & ChrW(44148) & vbCrLf & _
           ChrW(49892) & ChrW(54056) & ": " & failCount & ChrW(44148), vbInformation
    ' "이메일 발송 완료!" & "성공: N건" & "실패: N건"
End Sub

실습 2: 발송 상태 기록

발송 결과를 확인하고 미발송 건을 재발송하는 코드입니다.

ChatGPT 프롬프트

ChatGPT 프롬프트
엑셀 VBA 코드를 작성해줘.

- 현재 시트의 C열에 이메일 발송 상태가 기록되어 있어
- A열: 부서명, B열: 이메일, C열: 발송상태
- C열 값이 "완료"로 시작하는 행 수, "실패"로 시작하는 행 수, 빈 행 수를 세줘
- 결과를 MsgBox로 보여줘:
  - 전체: N건
  - 발송 완료: N건
  - 발송 실패: N건
  - 미발송: N건
- 한글 문자열은 ChrW()를 사용해줘

VBA 코드: 발송 상태 확인

VBA 코드
Sub CheckSendStatus()
    Dim ws As Worksheet
    Dim lastRow As Long
    Dim i As Long

    Dim totalCount As Long
    Dim successCount As Long
    Dim failCount As Long
    Dim pendingCount As Long

    Set ws = ActiveSheet
    lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row

    totalCount = 0
    successCount = 0
    failCount = 0
    pendingCount = 0

    For i = 2 To lastRow
        totalCount = totalCount + 1

        Dim statusVal As String
        statusVal = Trim(ws.Cells(i, 3).Value)

        If Left(statusVal, 2) = ChrW(50756) & ChrW(47308) Then
            ' "완료"로 시작
            successCount = successCount + 1
            ' 완료 행 배경색: 연한 녹색
            ws.Range("A" & i & ":C" & i).Interior.Color = RGB(198, 239, 206)
        ElseIf Left(statusVal, 2) = ChrW(49892) & ChrW(54056) Then
            ' "실패"로 시작
            failCount = failCount + 1
            ' 실패 행 배경색: 연한 빨강
            ws.Range("A" & i & ":C" & i).Interior.Color = RGB(255, 199, 206)
        Else
            ' 빈 칸 = 미발송
            pendingCount = pendingCount + 1
        End If
    Next i

    ' 결과 메시지
    Dim msg As String
    msg = ChrW(48156) & ChrW(49569) & " " & ChrW(49345) & ChrW(53468) & " " & ChrW(54869) & ChrW(51064) & vbCrLf & vbCrLf
    ' "발송 상태 확인"

    ' "전체: N건"
    msg = msg & ChrW(51204) & ChrW(52404) & ": " & totalCount & ChrW(44148) & vbCrLf
    ' "발송 완료: N건"
    msg = msg & ChrW(48156) & ChrW(49569) & " " & ChrW(50756) & ChrW(47308) & ": " & successCount & ChrW(44148) & vbCrLf
    ' "발송 실패: N건"
    msg = msg & ChrW(48156) & ChrW(49569) & " " & ChrW(49892) & ChrW(54056) & ": " & failCount & ChrW(44148) & vbCrLf
    ' "미발송: N건"
    msg = msg & ChrW(48120) & ChrW(48156) & ChrW(49569) & ": " & pendingCount & ChrW(44148)

    MsgBox msg, vbInformation, ChrW(48156) & ChrW(49569) & " " & ChrW(49345) & ChrW(53468)
    ' 제목: "발송 상태"
End Sub

실습 요약

단계작업 내용결과
1네이버 SMTP 설정 활성화POP3/SMTP 사용 설정 완료
2애플리케이션 비밀번호 생성SMTP 인증용 비밀번호 확보
3수신자 명단 작성A열: 부서명, B열: 이메일
4이메일 발송 VBA 실행부서별 파일 첨부 자동 발송
5발송 상태 확인성공/실패/미발송 건수 확인
6인증 정보 제거애플리케이션 비밀번호 삭제

보안 체크리스트

🚨
실습 완료 후 반드시 아래 항목을 확인하세요:
  • VBA 코드에 비밀번호가 직접 입력되어 있지 않은지 확인
  • 엑셀 파일에 비밀번호가 저장되어 있지 않은지 확인
  • 네이버 애플리케이션 비밀번호 삭제 또는 비활성화
  • ChatGPT 대화 기록에 실제 비밀번호가 포함되지 않았는지 확인
  • 테스트 발송 시 수신 주소를 본인 주소로 변경했는지 확인

주의사항

  1. 발송 테스트는 반드시 본인 주소로: 실습 시 B열의 모든 이메일을 본인 주소로 변경하고 테스트하세요. 타인에게 테스트 메일이 발송되지 않도록 주의합니다.
  2. 비밀번호는 InputBox로: 코드에 비밀번호를 직접 넣지 마세요. InputBox로 실행 시마다 입력받습니다.
  3. 발송 간격 유지: 네이버 SMTP는 짧은 시간에 대량 발송 시 차단될 수 있습니다. 각 발송 사이에 2초 이상 대기합니다.
  4. 첨부 파일 확인: 첨부할 파일이 지정된 경로에 존재하는지 먼저 확인하세요. 파일이 없으면 첨부 없이 발송됩니다.
  5. 실습 후 정리: 실습이 끝나면 애플리케이션 비밀번호를 삭제하고, POP3/SMTP 설정을 비활성화하는 것을 권장합니다.

실습 과제

  1. 네이버 SMTP 설정을 활성화하세요.
  2. 수신자 명단(5개 부서)을 엑셀에 작성하되, 이메일은 모두 본인 주소로 입력하세요.
  3. 이메일 발송 VBA를 실행하여 5건의 테스트 메일을 발송하세요.
  4. 발송 상태 확인 VBA로 결과를 점검하세요.
  5. 발송된 이메일을 열어 제목, 본문, 첨부파일이 올바른지 확인하세요.
  6. 실습 완료 후 보안 체크리스트를 점검하세요.

7차시. 파이썬을 이용한 MS 오피스 자동화

학습 목표

  • 파이썬으로 엑셀/파워포인트/이메일 발송까지 연결하는 자동화 흐름을 이해한다
  • VBA와 파이썬의 역할 차이를 구분한다
  • pandas, matplotlib, openpyxl, python-pptx, smtplib을 활용해 실습한다
  • 구글 코랩과 VS Code 두 가지 환경에서 동일한 결과를 만들어본다

1. 파이썬으로 오피스 자동화 개요

VBA vs 파이썬 비교

구분VBA파이썬
실행 위치엑셀/PPT/워드 내부외부 독립 실행
강점오피스 내부 자동화, 셀 서식 제어대량 데이터, 외부 파일 연동, 배치 작업
데이터 처리느림 (1만 행 이상 주의)빠름 (수십만 행도 가능)
시각화엑셀 차트에 의존matplotlib, seaborn 등 자유로운 시각화
이메일 발송CDO/Outlook 연동smtplib으로 직접 발송
학습 난이도엑셀 사용자에게 익숙초기 환경 설정 필요

월간 정기보고 자동화 시나리오

흐름도
[HR데이터.xlsx] → pandas 로딩 → 집계/시각화 → [엑셀 보고서] + [PPT 보고서] → 이메일 발송

자동화 가능한 작업의 조건:

  • 입력 파일 규칙이 일정하다 (같은 컬럼, 같은 형식)
  • 출력 양식이 고정되어 있다 (같은 서식, 같은 위치)
  • 반복 주기가 있다 (매주, 매월, 매분기)

실습 환경 안내

이 교재에서는 구글 코랩로컬 실행(VS Code / JupyterLab) 두 가지 환경을 병렬로 안내합니다. 두 환경 모두 .ipynb (Jupyter 노트북) 형식으로 실습 파일이 제공됩니다.

구분구글 코랩로컬 실행 (VS Code / JupyterLab)
데이터 보고서 실습실습파일/07-colab-version.ipynb실습파일/파이썬-MS오피스-자동화-local-version(데이터보고서).ipynb
수료증 생성 실습-실습파일/파이썬-MS오피스-자동화-local-version(수료증).ipynb
설치불필요 (웹 브라우저)Python + 패키지 설치 필요
파일 접근업로드 또는 드라이브 마운트실습파일/ 폴더에서 직접 접근
결과 저장코랩 환경에 저장 후 다운로드자동화실행결과/ 폴더에 자동 저장
파일명 규칙타임스탬프 포함 (YYYYMMDD_HHMMSS)타임스탬프 포함 (YYYYMMDD_HHMMSS)

2. 데이터 파일 로딩

구글 코랩에서

Python 코드 - 코랩 (방법 1: 파일 업로드)
# 방법 1: 파일 직접 업로드
from google.colab import files
uploaded = files.upload()  # 파일 선택 창이 열립니다

import pandas as pd
df = pd.read_excel('HR데이터.xlsx')
Python 코드 - 코랩 (방법 2: 드라이브 마운트)
# 방법 2: 구글 드라이브 마운트
from google.colab import drive
drive.mount('/content/drive')

import pandas as pd
df = pd.read_excel('/content/drive/MyDrive/HR데이터.xlsx')

로컬 실행에서 (VS Code / JupyterLab)

실습파일/파이썬-MS오피스-자동화-local-version(데이터보고서).ipynb 노트북을 VS Code 또는 JupyterLab에서 열어 실행합니다.

Python 코드 - 로컬
import pandas as pd
from pathlib import Path
from datetime import datetime

# HR데이터.xlsx는 같은 실습파일 폴더에 있음
df = pd.read_excel('HR데이터.xlsx')

# 결과 저장 폴더 설정 (자동화실행결과/)
OUTPUT_DIR = Path('../자동화실행결과')
OUTPUT_DIR.mkdir(exist_ok=True)

# 타임스탬프 생성 (파일명에 사용)
TIMESTAMP = datetime.now().strftime('%Y%m%d_%H%M%S')

3. 데이터 기본 탐색

아래 코드는 코랩과 VS Code에서 동일하게 사용합니다.

Python 코드
# 데이터 크기 확인
print(df.shape)  # (1470, 35)

# 처음 5행 미리보기
df.head()

# 전체 컬럼 정보
df.info()

# 기술 통계량
df.describe()

HR 데이터 구조:

  • 1,470명의 직원 데이터
  • 35개 한글 컬럼
  • 부서: 영업, 연구개발, 인사
  • 직무: 영업임원, 연구원, 실험기술자, 제조이사, 의료담당자, 관리자, 영업사원, 연구이사, 인사담당 (9종)

4. 데이터 유형 파악 및 수정

Python 코드
# 각 컬럼의 데이터 유형 확인
print(df.dtypes)

주요 컬럼별 데이터 유형

컬럼유형설명
나이int64숫자
퇴사여부object문자열 (예/아니오)
부서object문자열 (영업/연구개발/인사)
월급int64숫자
학력int64숫자 (1~5)
Python 코드 - 데이터 유형 변환
# 학력을 범주형으로 변환하는 예시
학력_매핑 = {1: '고졸이하', 2: '전문대졸', 3: '대졸', 4: '석사', 5: '박사'}
df['학력명'] = df['학력'].map(학력_매핑)

# 문자열을 범주형(category)으로 변환
df['부서'] = df['부서'].astype('category')
df['직무'] = df['직무'].astype('category')

print(df.dtypes)

5. 여러 가지 그래프 그리기

사전 설정

Python 코드 - 사전 설정
import matplotlib.pyplot as plt
import seaborn as sns
from datetime import datetime

# 한글 폰트 설정
plt.rcParams['font.family'] = 'Malgun Gothic'    # Windows
# plt.rcParams['font.family'] = 'AppleGothic'    # Mac
plt.rcParams['axes.unicode_minus'] = False

# 타임스탬프 (결과 파일명에 사용)
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
💡
구글 코랩 한글 폰트 설정: 코랩에서는 한글 폰트를 별도로 설치해야 합니다.
Python 코드 - 코랩 한글 폰트
!apt-get install -y fonts-nanum
import matplotlib.font_manager as fm
fm._rebuild()
plt.rcParams['font.family'] = 'NanumGothic'

5-1. 부서별 인원 막대그래프

Python 코드
dept_counts = df['부서'].value_counts()

plt.figure(figsize=(8, 5))
plt.bar(dept_counts.index, dept_counts.values, color=['#4472C4', '#ED7D31', '#70AD47'])
plt.title('부서별 인원 현황', fontsize=14)
plt.xlabel('부서')
plt.ylabel('인원 수')

for i, v in enumerate(dept_counts.values):
    plt.text(i, v + 10, str(v), ha='center', fontsize=12)

plt.tight_layout()
plt.savefig(f'chart-부서별인원_{timestamp}.png', dpi=150, bbox_inches='tight')
plt.show()
부서별 인원 현황 차트

5-2. 전공분야별 원형그래프

Python 코드
field_counts = df['전공분야'].value_counts()

plt.figure(figsize=(8, 6))
colors = ['#4472C4', '#ED7D31', '#A5A5A5', '#FFC000', '#70AD47', '#5B9BD5']
plt.pie(field_counts.values, labels=field_counts.index, autopct='%1.1f%%',
        colors=colors, startangle=90)
plt.title('전공분야별 분포', fontsize=14)
plt.tight_layout()
plt.savefig(f'chart-전공분야별분포_{timestamp}.png', dpi=150, bbox_inches='tight')
plt.show()
전공분야별 분포 차트

5-3. 통근거리 히스토그램

Python 코드
plt.figure(figsize=(8, 5))
plt.hist(df['통근거리'], bins=20, color='#4472C4', edgecolor='white')
plt.title('통근거리 분포', fontsize=14)
plt.xlabel('통근거리')
plt.ylabel('빈도')
plt.tight_layout()
plt.savefig(f'chart-통근거리분포_{timestamp}.png', dpi=150, bbox_inches='tight')
plt.show()
통근거리 분포 차트

5-4. 월급 박스플롯 (부서별)

Python 코드
plt.figure(figsize=(8, 5))
sns.boxplot(data=df, x='부서', y='월급', palette='Set2')
plt.title('부서별 월급 분포', fontsize=14)
plt.xlabel('부서')
plt.ylabel('월급')
plt.tight_layout()
plt.savefig(f'chart-부서별월급_{timestamp}.png', dpi=150, bbox_inches='tight')
plt.show()
직무별 인원 차트

5-5. 퇴사여부별 비교

Python 코드
fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# 퇴사여부별 인원
attrition = df['퇴사여부'].value_counts()
axes[0].bar(attrition.index, attrition.values, color=['#70AD47', '#ED7D31'])
axes[0].set_title('퇴사여부별 인원', fontsize=13)
for i, v in enumerate(attrition.values):
    axes[0].text(i, v + 10, str(v), ha='center', fontsize=12)

# 부서별 퇴사율
dept_attrition = df.groupby('부서')['퇴사여부'].apply(
    lambda x: (x == '예').mean() * 100
).sort_values(ascending=False)
axes[1].barh(dept_attrition.index, dept_attrition.values, color='#ED7D31')
axes[1].set_title('부서별 퇴사율 (%)', fontsize=13)
axes[1].set_xlabel('퇴사율 (%)')

plt.tight_layout()
plt.savefig(f'chart-퇴사현황_{timestamp}.png', dpi=150, bbox_inches='tight')
plt.show()
퇴사 현황 차트

6. 현황 데이터 집계

Python 코드 - 기본 집계
# 부서별 인원 집계
print("=== 부서별 인원 ===")
print(df['부서'].value_counts())

# 직무별 인원 집계
print("\n=== 직무별 인원 ===")
print(df['직무'].value_counts())

# 전공분야별 인원 집계
print("\n=== 전공분야별 인원 ===")
print(df['전공분야'].value_counts())
Python 코드 - 부서별 요약
# 부서별 평균 월급, 평균 나이
dept_summary = df.groupby('부서').agg(
    인원수=('사번', 'count'),
    평균월급=('월급', 'mean'),
    평균나이=('나이', 'mean'),
    퇴사율=('퇴사여부', lambda x: round((x == '예').mean() * 100, 1))
).reset_index()

print(dept_summary)
Python 코드 - 피벗테이블
# 피벗테이블: 부서 x 직무별 인원
pivot = pd.pivot_table(df, values='사번', index='부서', columns='직무',
                       aggfunc='count', fill_value=0)
print(pivot)

7. 집계 결과를 엑셀로 저장

📚
파일명 규칙: 실행 결과 파일에는 타임스탬프(YYYYMMDD_HHMMSS)를 포함하여 실행할 때마다 고유한 파일이 생성됩니다.

구글 코랩에서

Python 코드 - 코랩
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
from datetime import datetime

timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')

# 엑셀 파일 저장 (타임스탬프 포함)
output_file = f'HR_집계결과_{timestamp}.xlsx'

with pd.ExcelWriter(output_file, engine='openpyxl') as writer:
    dept_summary.to_excel(writer, sheet_name='부서별현황', index=False)
    pivot.to_excel(writer, sheet_name='부서직무별인원')
    df['전공분야'].value_counts().to_frame().to_excel(writer, sheet_name='전공분야별인원')

# 서식 적용
from openpyxl import load_workbook

wb = load_workbook(output_file)
ws = wb['부서별현황']

# 헤더 서식
header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')
header_font = Font(color='FFFFFF', bold=True, size=11)

for cell in ws[1]:
    cell.fill = header_fill
    cell.font = header_font
    cell.alignment = Alignment(horizontal='center')

# 열 너비 자동 조정
for col in ws.columns:
    max_len = max(len(str(cell.value or '')) for cell in col)
    ws.column_dimensions[col[0].column_letter].width = max_len + 4

wb.save(output_file)
print(f"엑셀 파일 저장 완료: {output_file}")

# 코랩에서 파일 다운로드
from google.colab import files
files.download(output_file)

로컬 실행에서

Python 코드 - 로컬
from openpyxl.styles import Font, Alignment, PatternFill, Border, Side
from pathlib import Path
from datetime import datetime

OUTPUT_DIR = Path('../자동화실행결과')
OUTPUT_DIR.mkdir(exist_ok=True)
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')

output_file = str(OUTPUT_DIR / f'HR_집계결과_{timestamp}.xlsx')

with pd.ExcelWriter(output_file, engine='openpyxl') as writer:
    dept_summary.to_excel(writer, sheet_name='부서별현황', index=False)
    pivot.to_excel(writer, sheet_name='부서직무별인원')
    df['전공분야'].value_counts().to_frame().to_excel(writer, sheet_name='전공분야별인원')

# 서식 적용
from openpyxl import load_workbook

wb = load_workbook(output_file)
ws = wb['부서별현황']

header_fill = PatternFill(start_color='4472C4', end_color='4472C4', fill_type='solid')
header_font = Font(color='FFFFFF', bold=True, size=11)

for cell in ws[1]:
    cell.fill = header_fill
    cell.font = header_font
    cell.alignment = Alignment(horizontal='center')

for col in ws.columns:
    max_len = max(len(str(cell.value or '')) for cell in col)
    ws.column_dimensions[col[0].column_letter].width = max_len + 4

wb.save(output_file)
print(f"엑셀 파일 저장 완료: {output_file}")

8. 결과 그래프를 PPT에 삽입

구글 코랩에서

Python 코드 - 코랩
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.enum.text import PP_ALIGN

prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)

# 표지 슬라이드
slide = prs.slides.add_slide(prs.slide_layouts[6])  # 빈 슬라이드
txBox = slide.shapes.add_textbox(Inches(2), Inches(2.5), Inches(9), Inches(2))
tf = txBox.text_frame
p = tf.paragraphs[0]
p.text = "HR 데이터 분석 보고서"
p.font.size = Pt(36)
p.font.bold = True
p.alignment = PP_ALIGN.CENTER

p2 = tf.add_paragraph()
p2.text = "파이썬 자동화 실습 결과"
p2.font.size = Pt(20)
p2.alignment = PP_ALIGN.CENTER

# 차트 이미지를 슬라이드에 삽입
chart_files = [
    (f'chart-부서별인원_{timestamp}.png', '부서별 인원 현황'),
    (f'chart-전공분야별분포_{timestamp}.png', '전공분야별 분포'),
    (f'chart-통근거리분포_{timestamp}.png', '통근거리 분포'),
    (f'chart-퇴사현황_{timestamp}.png', '퇴사 현황 분석'),
]

for chart_file, title in chart_files:
    slide = prs.slides.add_slide(prs.slide_layouts[6])

    # 제목 추가
    txBox = slide.shapes.add_textbox(Inches(0.5), Inches(0.3), Inches(12), Inches(0.8))
    tf = txBox.text_frame
    p = tf.paragraphs[0]
    p.text = title
    p.font.size = Pt(24)
    p.font.bold = True

    # 차트 이미지 삽입
    slide.shapes.add_picture(chart_file, Inches(1.5), Inches(1.5), Inches(10), Inches(5.5))

pptx_file = f'HR_분석보고서_{timestamp}.pptx'
prs.save(pptx_file)
print(f"PPT 저장 완료: {pptx_file}")

# 코랩에서 파일 다운로드
from google.colab import files
files.download(pptx_file)

로컬 실행에서

Python 코드 - 로컬
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.enum.text import PP_ALIGN

prs = Presentation()
prs.slide_width = Inches(13.333)
prs.slide_height = Inches(7.5)

# 표지 슬라이드
slide = prs.slides.add_slide(prs.slide_layouts[6])
txBox = slide.shapes.add_textbox(Inches(2), Inches(2.5), Inches(9), Inches(2))
tf = txBox.text_frame
p = tf.paragraphs[0]
p.text = "HR 데이터 분석 보고서"
p.font.size = Pt(36)
p.font.bold = True
p.alignment = PP_ALIGN.CENTER

p2 = tf.add_paragraph()
p2.text = "파이썬 자동화 실습 결과"
p2.font.size = Pt(20)
p2.alignment = PP_ALIGN.CENTER

# 차트 이미지를 슬라이드에 삽입 (자동화실행결과 폴더에서 읽기)
chart_files = [
    (f'chart-부서별인원_{timestamp}.png', '부서별 인원 현황'),
    (f'chart-전공분야별분포_{timestamp}.png', '전공분야별 분포'),
    (f'chart-통근거리분포_{timestamp}.png', '통근거리 분포'),
    (f'chart-퇴사현황_{timestamp}.png', '퇴사 현황 분석'),
]

for chart_file, title in chart_files:
    chart_path = OUTPUT_DIR / chart_file
    slide = prs.slides.add_slide(prs.slide_layouts[6])

    txBox = slide.shapes.add_textbox(Inches(0.5), Inches(0.3), Inches(12), Inches(0.8))
    tf = txBox.text_frame
    p = tf.paragraphs[0]
    p.text = title
    p.font.size = Pt(24)
    p.font.bold = True

    slide.shapes.add_picture(str(chart_path), Inches(1.5), Inches(1.5), Inches(10), Inches(5.5))

pptx_file = str(OUTPUT_DIR / f'HR_분석보고서_{timestamp}.pptx')
prs.save(pptx_file)
print(f"PPT 저장 완료: {pptx_file}")

9. PPT를 이메일로 자동 발송

구글 코랩에서

Python 코드 - 코랩
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders

# ===== 발송 설정 =====
smtp_server = 'smtp.naver.com'
smtp_port = 587
sender_email = 'your_id@naver.com'        # 본인 네이버 아이디
sender_password = 'your_app_password'       # 앱 비밀번호 (네이버 설정에서 생성)
receiver_email = 'receiver@example.com'     # 수신자 이메일

# ===== 이메일 구성 =====
msg = MIMEMultipart()
msg['From'] = sender_email
msg['To'] = receiver_email
msg['Subject'] = 'HR 데이터 분석 보고서 - 자동 발송'

body = """안녕하세요,

HR 데이터 분석 보고서를 첨부하여 보내드립니다.
파이썬 자동화를 통해 생성된 보고서입니다.

감사합니다.
"""
msg.attach(MIMEText(body, 'plain', 'utf-8'))

# ===== 파일 첨부 =====
pptx_file = 'HR_분석보고서.pptx'
with open(pptx_file, 'rb') as f:
    part = MIMEBase('application', 'octet-stream')
    part.set_payload(f.read())
    encoders.encode_base64(part)
    part.add_header('Content-Disposition', f'attachment; filename="{pptx_file}"')
    msg.attach(part)

# ===== 발송 =====
try:
    server = smtplib.SMTP(smtp_server, smtp_port)
    server.starttls()
    server.login(sender_email, sender_password)
    server.send_message(msg)
    server.quit()
    print("이메일 발송 완료!")
except Exception as e:
    print(f"발송 실패: {e}")

VS Code에서

Python 코드 - VS Code
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders

# ===== 발송 설정 =====
smtp_server = 'smtp.naver.com'
smtp_port = 587
sender_email = 'your_id@naver.com'
sender_password = 'your_app_password'
receiver_email = 'receiver@example.com'

# ===== 이메일 구성 =====
msg = MIMEMultipart()
msg['From'] = sender_email
msg['To'] = receiver_email
msg['Subject'] = 'HR 데이터 분석 보고서 - 자동 발송'

body = """안녕하세요,

HR 데이터 분석 보고서를 첨부하여 보내드립니다.
파이썬 자동화를 통해 생성된 보고서입니다.

감사합니다.
"""
msg.attach(MIMEText(body, 'plain', 'utf-8'))

# ===== 파일 첨부 =====
pptx_file = 'HR_분석보고서.pptx'
with open(pptx_file, 'rb') as f:
    part = MIMEBase('application', 'octet-stream')
    part.set_payload(f.read())
    encoders.encode_base64(part)
    part.add_header('Content-Disposition', f'attachment; filename="{pptx_file}"')
    msg.attach(part)

# ===== 발송 =====
try:
    server = smtplib.SMTP(smtp_server, smtp_port)
    server.starttls()
    server.login(sender_email, sender_password)
    server.send_message(msg)
    server.quit()
    print("이메일 발송 완료!")
except Exception as e:
    print(f"발송 실패: {e}")

10. 보안 주의사항

🚨
반드시 지켜야 할 보안 원칙:
  1. 아이디와 비밀번호를 코드에 직접 입력한 경우, 실습 후 반드시 삭제합니다
  2. 네이버 앱 비밀번호를 사용합니다 (계정 비밀번호가 아님)
  3. 코드를 공유하거나 깃허브에 올릴 때 인증 정보가 포함되지 않았는지 확인합니다
  4. 실습 시 수신 이메일을 본인 주소로 설정하여 테스트합니다
  5. 실습이 끝나면 네이버 설정에서 앱 비밀번호를 삭제 또는 비활성화합니다

네이버 SMTP 설정 방법

  1. 네이버 메일 접속 → 환경설정 → POP3/IMAP 설정
  2. POP3/SMTP 사용 → 사용함으로 변경
  3. 네이버 내 정보 → 보안 설정 → 2단계 인증 활성화
  4. 애플리케이션 비밀번호 생성 → 생성된 비밀번호를 코드에 사용

10-1. (추가 실습) 파이썬으로 수료증 PDF 자동 생성

4차시에서 VBA로 구현한 수료증 PDF 생성을 파이썬으로도 할 수 있습니다.

실습 파일: 실습파일/파이썬-MS오피스-자동화-local-version(수료증).ipynb

Python 코드 - 핵심 3줄
from pptx import Presentation

# 핵심 코드 3줄
prs = Presentation('실습_교육수료증-템플릿.pptx')   # 템플릿 열기
replace_placeholders(prs.slides[0], data)           # 치환
prs.save('수료증_김민수.pptx')                       # 저장

동작 방식:

  1. python-pptx로 템플릿의 {{이름}}, {{사번}} 등 플레이스홀더를 치환하여 개인별 PPTX 생성
  2. VBScript(cscript)로 PowerPoint의 SaveAs(경로, 32) 호출하여 일괄 PDF 변환
  3. 임시 PPTX 파일 삭제

VBA vs Python 비교

항목VBAPython
실행 환경PowerPoint 매크로Jupyter Notebook
템플릿 형식.pptm.pptx
PDF 변환ExportAsFixedFormatVBScript 일괄 변환
확장성PowerPoint 한정pandas 등과 연동 가능

10-2. (참고) 같은 기능을 VBA로도 구현 가능

7차시에서 파이썬으로 만든 엑셀 보고서PPT 보고서 기능은 엑셀 VBA로도 구현되어 있습니다.

실습 파일: 실습파일/HR데이터(VBA포함).xlsm — M11, M12 모듈

모듈함수명기능
M11_엑셀보고서생성HR_엑셀보고서_생성()부서별현황, 부서직무별인원, 전공분야별인원 3개 시트를 가진 엑셀 보고서 생성
M12_PPT보고서생성HR_PPT보고서_생성()5종 차트를 자동 생성하고 PPT 슬라이드에 삽입

저장 위치: 자동화실행결과/자동화결과_엑셀VBA로-보고서출력/

실행 방법: HR데이터(VBA포함).xlsm을 열고 Alt+F8 → 함수명 선택 → 실행

정리

이번 차시에서 실습한 전체 자동화 흐름:

전체 자동화 파이프라인
1. HR데이터.xlsx 로딩 (pandas)
   ↓
2. 데이터 유형 확인 및 수정
   ↓
3. 시각화 (matplotlib/seaborn) → 차트 이미지 저장
   ↓
4. 데이터 집계 (groupby, pivot_table)
   ↓
5. 집계 결과 → 엑셀 파일 저장 (openpyxl)
   ↓
6. 차트 이미지 → PPT 슬라이드 삽입 (python-pptx)
   ↓
7. PPT 파일 → 이메일 첨부 발송 (smtplib)

핵심 라이브러리 정리

라이브러리용도설치 명령
pandas데이터 로딩/집계pip install pandas
matplotlib그래프 생성pip install matplotlib
seaborn통계 시각화pip install seaborn
openpyxl엑셀 읽기/쓰기/서식pip install openpyxl
python-pptxPPT 생성/편집pip install python-pptx
smtplib이메일 발송기본 내장 (설치 불필요)

실습 과제

"HR 데이터 → 분석 → 엑셀 저장 → PPT 생성 → 이메일 발송" 전체 파이프라인을 완성하세요.

  1. HR데이터.xlsx를 로딩하여 부서별/직무별/전공별 집계를 수행하세요
  2. 차트 5종을 생성하고 이미지 파일로 저장하세요
  3. 집계 결과를 엑셀 파일로 저장하세요 (헤더 서식 포함)
  4. 차트 이미지를 PPT 슬라이드에 삽입하세요
  5. 완성된 PPT를 이메일로 발송하세요 (본인 주소로 테스트)